Relocate the ~6k-file docs tree from the repo root into plugins/document-navigator/docs/ so the plugin is self-contained and portable. docs/superpowers/ (specs and plans) stays at the repo root. Moved via cp+rm rather than git mv because directory rename() fails with EPERM on this WSL /mnt DrvFs mount; git rename-detection pairs the identical-content files at commit time. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
14 KiB
14 KiB
@suaveplan/cli - Zero-dependency CLI toolkit: argument parser, command router, ANSI styling, output formatting, spinner, progress bar, interactive prompts
Summary
Enterprise-grade CLI framework for Node.js. Composable fluent API with subcommand routing, option parsing, OTel tracing, and a full set of terminal UI primitives.
Documentation tree
- app — (no submodules)
- app — (no submodules)
- command — (no submodules)
- command — (no submodules)
- errors — (no submodules)
- errors — (no submodules)
- format — (no submodules)
- format — (no submodules)
- table — (no submodules)
- table — (no submodules)
- parser — (no submodules)
- parser — (no submodules)
- progress — (no submodules)
- progress — (no submodules)
- prompt — (no submodules)
- prompt — (no submodules)
- spinner — (no submodules)
- spinner — (no submodules)
- style — (no submodules)
- style — (no submodules)
Modules
| Module | Path | Description |
|---|---|---|
| App / cli() | app/app |
Application entry point and root command |
| Command | command/command |
Subcommand builder with fluent API |
| Parser | parser/parser |
Argv parser with options, positionals, and short flags |
| Errors | errors/errors |
Stable error codes and CliError class |
| Style | style/style |
ANSI color and formatting with chainable proxy styler |
| Format | output/format/format |
Text formatters: key-value, list, tree, columns, heading |
| Table | output/table/table |
ASCII/Unicode bordered table renderer |
| Spinner | spinner/spinner |
TTY-aware animated spinner with AbortSignal |
| Progress | progress/progress |
ASCII progress bar with TTY in-place update |
| Prompt | prompt/prompt |
Interactive prompts: text, password, confirm, select, multiselect |
Quick Start
import { cli } from "@suaveplan/cli";
const app = cli("myapp", "1.0.0");
app.description("My enterprise CLI tool");
app
.command("build")
.description("Build the project")
.option("--output <dir>, -o", "output directory", "./dist")
.option("--watch, -w", "watch for changes")
.action(async (ctx) => {
const { output, watch } = ctx.options;
console.log(`Building to ${output}${watch ? " (watch mode)" : ""}...`);
});
app
.command("deploy")
.description("Deploy to production")
.argument("<env>", "target environment")
.action(async (ctx) => {
const env = ctx.args["env"];
console.log(`Deploying to ${env}...`);
});
await app.run();
App / cli() Factory
import { App, cli } from "@suaveplan/cli";
// Factory shorthand
const app = cli("myapp", "1.0.0");
// Or full constructor
const app = new App({
name: "myapp",
version: "1.0.0",
description: "My tool",
writer: process.stdout.write.bind(process.stdout),
onError: (err) => { console.error(err); process.exit(1); },
});
app.action(async (ctx) => { /* root action */ });
await app.run(process.argv.slice(2));
Options: enableVersion (default true) and enableHelp (default true) control whether --version and --help flags are intercepted. Pass { signal: AbortSignal } to run() to support cancellation.
Commands
const cmd = app
.command("serve")
.description("Start the dev server")
.alias("s")
.option("--port <n>, -p", "port number", "3000")
.argument("[dir]", "root directory")
.example("myapp serve --port 8080 ./src")
.action(async (ctx) => {
const port = ctx.options["port"];
const dir = ctx.args["dir"] ?? ".";
console.log(`Serving ${dir} on port ${port}`);
});
// Nest subcommands arbitrarily deep
const db = app.command("db").description("Database utilities");
db.command("migrate").action(async () => { /* ... */ });
db.command("seed").hidden().action(async () => { /* ... */ });
Terminal UI
Spinner
import { createSpinner } from "@suaveplan/cli";
const sp = createSpinner({ text: "Building…" });
sp.start();
// later…
sp.succeed("Build complete");
// or
sp.fail("Build failed");
Progress Bar
import { createProgress } from "@suaveplan/cli";
const bar = createProgress({ total: 100, label: "Compiling" });
bar.update(42);
bar.complete("Done");
Interactive Prompts
import { text, password, confirm, select, multiselect } from "@suaveplan/cli";
const name = await text("Your name?");
const pass = await password({ message: "Password?", validate: (v) => v.length >= 8 || "too short" });
const ok = await confirm("Continue?", reader);
const env = await select({ message: "Environment:", choices: ["dev", "staging", "prod"] });
const tags = await multiselect({ message: "Tags:", choices: ["a", "b", "c"], min: 1, max: 3 });
Style & Formatting
import { style, createStyler, heading, list, table } from "@suaveplan/cli";
console.log(style.bold.green("Success!"));
console.log(heading("Deploy Report", 1));
console.log(list(["Step 1", "Step 2", "Step 3"]));
console.log(table(["Name", "Status"], [["api", "ok"], ["db", "ok"]]));
Error Codes
All errors are CliError instances with stable string codes:
| Code | Meaning |
|---|---|
cli.app.aborted |
Run cancelled via AbortSignal |
cli.app.run_failed |
Uncaught error in action |
cli.command.no_action |
Command has no action defined |
cli.command.unknown_subcommand |
No matching subcommand found |
cli.parse.unknown_option |
Unrecognised flag |
cli.parse.missing_value |
Flag expects a value that wasn't provided |
cli.parse.missing_argument |
Required positional argument missing |
cli.parse.invalid_value |
Value not in allowed choices or wrong type |
cli.parse.missing_required_option |
Required option not provided |
OTel
Every App.run() and Command.execute() call is wrapped in an OpenTelemetry span. Pass a tracer via @opentelemetry/api — no-op tracer is used by default, so no configuration is required.