Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 

README.md

@rcompat/cli

CLI tools for terminal colors, formatting, and interactive prompts.

What is @rcompat/cli?

A cross-runtime module providing terminal styling, output formatting, and interactive prompts. Build beautiful command-line interfaces that work consistently across Node, Deno, and Bun.

Installation

npm install @rcompat/cli
pnpm add @rcompat/cli
yarn add @rcompat/cli
bun add @rcompat/cli

Usage

Colors

Style terminal output with ANSI colors.

import color from "@rcompat/cli/color";

console.log(color.red("Error: something went wrong"));
console.log(color.green("Success!"));
console.log(color.blue("Info: processing..."));
console.log(color.bold("Important message"));
console.log(color.dim("Less important"));

// combine styles
console.log(color.bold(color.red("Critical error!")));

Available colors:

  • red, green, blue, cyan, magenta, yellow
  • black, white, gray
  • bold, dim, inverse

print

Write to stdout without a newline.

import print from "@rcompat/cli/print";

print("Loading");
print(".", ".", ".");  // Multiple arguments joined with space
print("\n");

mark

Format strings with placeholders and dim styling.

import mark from "@rcompat/cli/mark";

console.log(mark("Created {0} in {1}", "config.json", "./src"));
// Output: Created config.json in ./src (values dimmed)

console.log(mark("Found {0} files", 42));
// Output: Found 42 files

Prompts

Interactive CLI prompts for user input.

text

import text from "@rcompat/cli/prompts/text";
import isCancel from "@rcompat/cli/prompts/is-cancel";

const name = await text({
  message: "What is your name?",
  initial: "Anonymous",
  validate: (value) => {
    if (value.length < 2) return "Name must be at least 2 characters";
  },
});

if (isCancel(name)) {
  console.log("Cancelled");
  process.exit(0);
}

console.log(`Hello, ${name}!`);

confirm

import confirm from "@rcompat/cli/prompts/confirm";
import isCancel from "@rcompat/cli/prompts/is-cancel";

const proceed = await confirm({
  message: "Do you want to continue?",
  initial: true,  // default to yes
});

if (isCancel(proceed)) {
  console.log("Cancelled");
  process.exit(0);
}

if (proceed) {
  console.log("Continuing...");
}

select

import select from "@rcompat/cli/prompts/select";

const color = await select({
  message: "Pick a color",
  options: [
    { label: "Red", value: "#ff0000" },
    { label: "Green", value: "#00ff00" },
    { label: "Blue", value: "#0000ff" },
  ],
  initial: 0,  // default to first option
});

console.log(`You picked: ${color}`);

multiselect

import multiselect from "@rcompat/cli/prompts/multiselect";

const features = await multiselect({
  message: "Select features to install",
  options: [
    { label: "TypeScript", value: "typescript" },
    { label: "ESLint", value: "eslint" },
    { label: "Prettier", value: "prettier" },
    { label: "Testing", value: "testing" },
  ],
  initial: [0, 1],  // pre-select first two
});

console.log(`Installing: ${features.join(", ")}`);

spinner

import spinner from "@rcompat/cli/prompts/spinner";

const s = spinner();

s.start("Installing dependencies");

// Update message while running
s.message("Still installing...");

// Simulate async work
await new Promise(r => setTimeout(r, 2000));

s.stop("Dependencies installed");

intro / outro

import intro from "@rcompat/cli/prompts/intro";
import outro from "@rcompat/cli/prompts/outro";

intro("Welcome to the setup wizard");

// ... your prompts here ...

outro("Setup complete!");

cancel

Handle user cancellation (Ctrl+C).

import text from "@rcompat/cli/prompts/text";
import isCancel from "@rcompat/cli/prompts/is-cancel";
import cancel from "@rcompat/cli/prompts/cancel";

const name = await text({ message: "Your name?" });

if (isCancel(name)) {
  cancel("Operation cancelled");
  process.exit(0);
}

API Reference

Colors

import color from "@rcompat/cli/color";
declare function color.[NAME](message: string): string;

print

declare function print(...messages: string[]): void;

Writes messages to stdout, joined by spaces, without a trailing newline.

mark

declare function mark(format: string, ...params: unknown[]): string;

Formats a string by replacing {0}, {1}, etc. with dimmed parameter values.

Prompts

text

declare function text(options: TextOptions): Promise<string | CancelSymbol>;

interface TextOptions {
  message: string;
  initial?: string;
  validate?: (input: string) => string | void | Promise<string | void>;
}

confirm

declare function confirm(options: ConfirmOptions): Promise<boolean | CancelSymbol>;

interface ConfirmOptions {
  message: string;
  initial?: boolean;
}

select

declare function select<T>(options: SelectOptions<T>): Promise<T>;

interface SelectOptions<T> {
  message: string;
  options: Array<{ label: string; value: T }>;
  initial?: number;
}

multiselect

declare function multiselect<T>(options: MultiselectOptions<T>): Promise<T[]>;

interface MultiselectOptions<T> {
  message: string;
  options: Array<{ label: string; value: T }>;
  initial?: number[];
}

spinner

declare function spinner(): {
  start(message: string): void;
  message(text: string): void;
  stop(message?: string): void;
};

intro / outro / cancel

declare function intro(message?: string): void;
declare function outro(message?: string): void;
declare function cancel(message?: string): CancelSymbol;

isCancel

declare function isCancel(value: unknown): value is CancelSymbol;

Examples

Interactive CLI wizard

import intro from "@rcompat/cli/prompts/intro";
import outro from "@rcompat/cli/prompts/outro";
import text from "@rcompat/cli/prompts/text";
import select from "@rcompat/cli/prompts/select";
import confirm from "@rcompat/cli/prompts/confirm";
import spinner from "@rcompat/cli/prompts/spinner";
import isCancel from "@rcompat/cli/prompts/is-cancel";
import cancel from "@rcompat/cli/prompts/cancel";
import green from "@rcompat/cli/color/green";

intro("Project Setup");

const name = await text({
  message: "Project name?",
  initial: "my-app",
});
if (isCancel(name)) {
  cancel("Setup cancelled");
  process.exit(0);
}

const template = await select({
  message: "Select a template",
  options: [
    { label: "Minimal", value: "minimal" },
    { label: "Full", value: "full" },
  ],
});

const install = await confirm({
  message: "Install dependencies?",
  initial: true,
});

if (install) {
  const s = spinner();
  s.start("Installing dependencies");
  await new Promise(r => setTimeout(r, 2000));
  s.stop("Dependencies installed");
}

outro(green(`Created ${name} with ${template} template`));

Colored log levels

import color from "@rcompat/cli/color";

const log = {
  error: (msg) => console.log(color.red("ERROR"), msg),
  warn: (msg) => console.log(color.yellow("WARN"), msg),
  info: (msg) => console.log(color.blue("INFO"), msg),
  debug: (msg) => console.log(color.dim("DEBUG"), color.dim(msg)),
};

log.error("Connection failed");
log.warn("Deprecated API");
log.info("Server started");
log.debug("Request received");

Cross-Runtime Compatibility

Runtime Supported
Node.js
Deno
Bun

No configuration required — just import and use.

License

MIT

Contributing

See CONTRIBUTING.md in the repository root.