Gathering detailed insights and metrics for @reliverse/prompts
Gathering detailed insights and metrics for @reliverse/prompts
Gathering detailed insights and metrics for @reliverse/prompts
Gathering detailed insights and metrics for @reliverse/prompts
🐦🔥 @reliverse/rempts is a modern, type-safe toolkit for building delightful cli experiences. it's fast, flexible, and made for developer happiness. file-based commands keep things simple—no clutter, just clean and easy workflows. this is how cli should feel.
npm install @reliverse/prompts
Typescript
Module System
Node Version
NPM Version
TypeScript (99.42%)
JavaScript (0.58%)
Total Downloads
0
Last Day
0
Last Week
0
Last Month
0
Last Year
0
MIT License
13 Stars
105 Commits
1 Forks
1 Branches
1 Contributors
Updated on Jul 08, 2025
Latest Version
1.6.1
Package Id
@reliverse/prompts@1.6.1
Unpacked Size
200.77 kB
Size
48.42 kB
File Count
89
NPM Version
10.8.3
Node Version
22.6.0
Published on
Apr 02, 2025
Cumulative downloads
Total Downloads
Last Day
0%
NaN
Compared to previous day
Last Week
0%
NaN
Compared to previous week
Last Month
0%
NaN
Compared to previous month
Last Year
0%
NaN
Compared to previous year
21
sponsor — discord — repo — npm
@reliverse/rempts is a modern, type-safe toolkit for building delightful cli experiences. it's fast, flexible, and made for developer happiness. file-based commands keep things simple—no clutter, just clean and easy workflows. this is how cli should feel.
unjs/citty
and @clack/prompts
inquirer
/citty
/commander
/chalk
bun dler rempts --init cmd1 cmd2
)src/app/cmds.ts
file (bun dler rempts
)1bun add @reliverse/rempts
All main prompts APIs are available from the package root:
1import { 2 // ...prompts 3 inputPrompt, selectPrompt, multiselectPrompt, numberPrompt, 4 confirmPrompt, togglePrompt, taskSpinPrompt, taskProgressPrompt, 5 startPrompt, endPrompt, resultPrompt, nextStepsPrompt, 6 // ...hooks 7 useSpinner, 8 // ...launcher 9 createCli, defineCommand, defineArgs, 10 // ...types 11 // ...more 12} from "@reliverse/rempts";
See
src/mod.ts
for the full list of exports.
Prompt | Description |
---|---|
useSpinner | Start/stop spinner |
inputPrompt | Single-line input (with mask support, e.g. for passwords) |
selectPrompt | Single-choice radio menu |
multiselectPrompt | Multi-choice checkbox menu |
numberPrompt | Type-safe number input |
confirmPrompt | Yes/No toggle |
togglePrompt | Custom on/off toggles |
taskProgressPrompt | Progress bar for async tasks |
resultPrompt | Show results in a styled box |
nextStepsPrompt | Show next steps in a styled list |
startPrompt /endPrompt | Makes CLI start/end flows look nice |
taskSpinPrompt | Async loader with spinner (possibly will be deprecated) |
datePrompt | Date input with format validation |
anykeyPrompt | Wait for any keypress |
To help you migrate from the different CLI frameworks, @reliverse/rempts
has some aliases for the most popular prompts.
Prompt | Aliases |
---|---|
createCli | runMain |
onCmdInit | setup |
onCmdExit | cleanup |
useSpinner | spinner |
selectPrompt | select |
multiselectPrompt | multiselect |
inputPrompt | text , input |
confirmPrompt | confirm |
introPrompt | intro , start |
outroPrompt | outro , end |
log | relinka |
1import { relinka } from "@reliverse/relinka"; 2 3import { 4 startPrompt, 5 inputPrompt, 6 selectPrompt, 7 defineCommand, 8 runMain 9} from "@reliverse/rempts"; 10 11async function main() { 12 await startPrompt({ title: "Project Setup" }); 13 14 const name = await inputPrompt({ 15 title: "What's your project name?", 16 defaultValue: "my-cool-project", 17 }); 18 19 const spinner = useSpinner({ 20 text: "Loading...", 21 indicator: "timer", // or "dots" 22 frames: ["◒", "◐", "◓", "◑"], // custom frames 23 delay: 80, // custom delay 24 onCancel: () => { 25 console.log("Operation cancelled"); 26 }, 27 cancelMessage: "Operation cancelled by user", 28 errorMessage: "Operation failed", 29 signal: abortController.signal, 30 }).start(); 31 32 // The spinner will show: 33 // ◒ Loading... [5s] 34 // With animated frames and timer 35 36 const framework = await selectPrompt({ 37 title: "Pick your framework", 38 options: [ 39 { value: "next", label: "Next.js" }, 40 { value: "svelte", label: "SvelteKit" }, 41 { value: "start", label: "TanStack Start" }, 42 ], 43 defaultValue: "next", 44 }); 45 46 console.log("Your result:", { name, framework }); 47}; 48 49await main();
Available spinner options:
Option | Description |
---|---|
cancelMessage | The message to display when the spinner is cancelled |
color | The color of the spinner |
delay | The delay between frames |
errorMessage | The message to display when the spinner fails |
failText | The text to display when the spinner fails |
frames | The frames to use for the spinner |
hideCursor | Whether to hide the cursor |
indicator | The indicator to use for the spinner |
onCancel | The function to call when the spinner is cancelled |
prefixText | The text to display before the spinner |
signal | The signal to use for the spinner |
silent | Whether to hide the spinner |
spinner | The spinner to use for the spinner |
successText | The text to display when the spinner succeeds |
text | The text to display next to the spinner |
Available indicator options:
Option | Description |
---|---|
timer | The timer indicator |
dots | The dots indicator |
Available signal options:
Option | Description |
---|---|
abortController.signal | The signal to use for the spinner |
Available frames options:
Option | Description |
---|---|
["◒", "◐", "◓", "◑"] | The frames to use for the spinner |
Available delay options:
Option | Description |
---|---|
80 | The delay between frames |
Available onCancel options:
Option | Description |
---|---|
() => { console.log("Operation cancelled"); } | The function to call when the spinner is cancelled |
Note:
runMain
is now an alias forcreateCli
and is still supported for backward compatibility. The newcreateCli
API provides a more intuitive object-based configuration format.
1bun add -D @reliverse/dler 2bun dler rempts --init cmd1 cmd2 # creates `src/app/cmd1/cmd.ts` and `src/app/cmd2/cmd.ts` files 3bun dler rempts # creates `src/app/cmds.ts` file
Important: Ensure your commands don't have await main();
, await createCli();
, or something like that — to prevent any unexpected behavior. Only main command should have it.
1import { relinka } from "@reliverse/relinka";
2
3import { defineCommand, createCli } from "@reliverse/rempts";
4
5const main = defineCommand({
6 meta: {
7 name: "rempts",
8 version: "1.0.0",
9 description: "Rempts Launcher Playground CLI",
10 },
11 onCmdInit() {
12 relinka("success", "Setup");
13 },
14 onCmdExit() {
15 relinka("success", "Cleanup");
16 },
17 commands: {
18 build: () => import("./app/build/cmd.js").then((r) => r.default),
19 deploy: () => import("./app/deploy/cmd.js").then((r) => r.default),
20 debug: () => import("./app/debug/cmd.js").then((r) => r.default),
21 },
22});
23
24// New object format (recommended)
25await createCli({
26 mainCommand: main,
27 fileBased: {
28 enable: true,
29 cmdsRootPath: "my-cmds", // default is `./app`
30 },
31 // Optionally disable auto-exit to handle errors manually:
32 autoExit: false,
33});
34
35// Legacy format (still supported)
36await createCli(main, {
37 fileBased: {
38 enable: true,
39 cmdsRootPath: "my-cmds", // default is `./app`
40 },
41 // Optionally disable auto-exit to handle errors manually:
42 autoExit: false,
43});
This flexibility allows you to easily build a rich, multi-command CLI with minimal boilerplate. The launcher even supports nested commands, making it simple to construct complex CLI applications.
Drop a ./src/cli/app/add/index.ts
and it's live.
1import { defineArgs, defineCommand } from "@reliverse/rempts"; 2export default defineCommand({ 3 meta: { 4 name: "add", 5 version: "1.0.0", 6 description: "Add stuff to your project", 7 }, 8 args: { 9 name: defineArgs({ // 💡 PRO TIP: use defineArgs() to get fully correct intellisense 10 type: "string", 11 required: true, 12 description: "Name of what to add", 13 }), 14 }, 15 async run({ args }) { 16 relinka("log", "Adding:", args.name); 17 }, 18});
Supports:
arg-cmdName.{ts,js}
,cmdName/index.{ts,js}
,cmdName/cmdName-mod.{ts,js}
,foo/bar/baz/cmd.ts
→ my-cli foo bar baz
Hint:
bun add -D @reliverse/dler
bun dler rempts --init cmd1 cmd2
to init commands for rempts launcher's automatically1defineCommand({ 2 meta: { name: "cli", version: "1.0.0" }, 3 args: { 4 name: { type: "string", required: true }, 5 verbose: { type: "boolean", default: false }, 6 animals: { type: "array", default: ["cat","dog"] }, 7 }, 8 async run({ args, raw }) { // or `async run(ctx)` 9 relinka("log", args.name, args.verbose, args.animals); // or `relinka("log", ctx.args.name, ...);` 10 }, 11});
Supports:
positional
argsarray
types (--tag foo --tag bar
)By the way! Multi-level subcommands!
You can also nest subcommands arbitrarily deep:
1app/ 2 foo/ 3 bar/ 4 baz/ 5 cmd.ts
Invoke with:
1my-cli foo bar baz --some-flag
The launcher will recursively traverse subfolders for each non-flag argument, loading the deepest cmd.ts
/cmd.js
it finds, and passing the remaining arguments to it.
See example/launcher/app/nested and example/launcher/app/sibling folders to learn more.
When playing with the example, you can run e.g. bun dev:modern nested foo bar baz
to see the result in action.
Rempts now supports seamless integration with tRPC and ORPC routers, allowing you to automatically generate CLI commands from your RPC procedures. This provides a powerful way to expose your API endpoints as command-line tools.
1import { z } from "zod"; 2import { initTRPC } from "@trpc/server"; 3import { createCli } from "@reliverse/rempts"; 4 5const t = initTRPC.create(); 6 7const appRouter = t.router({ 8 hello: t.procedure 9 .input(z.object({ name: z.string().optional() })) 10 .query(({ input }) => `Hello ${input.name ?? "World"}!`), 11 12 add: t.procedure 13 .input(z.object({ a: z.number(), b: z.number() })) 14 .mutation(({ input }) => input.a + input.b) 15}); 16 17// Automatically generates CLI commands from your tRPC procedures 18await createCli({ 19 name: "my-cli", 20 rpc: { router: appRouter } 21});
Features:
See RPC Integration Guide for detailed documentation and examples.
1git clone https://github.com/reliverse/rempts 2cd rempts 3bun i 4bun dev
bun dev:prompts
: This example will show you a multiselectPrompt()
where you can choose which CLI prompts you want to play with.bun dev:modern
: This example will show you a modern CLI launcher usage with file-based commands.bun dev:classic
: This example will show you a classic CLI launcher usage with programmatic commands.1bun example/trpc-orpc/rempts/effect-primary.ts create-profile --name 'Jane Smith' --age 28 --bio 'Software Engineer' --tags 'developer,typescript'
1 Create a src/mod.ts
file:
1import { createCli, defineCommand } from "@reliverse/rempts"; 2 3// New object format (recommended) 4await createCli({ 5 mainCommand: defineCommand({}), 6}); 7 8// Legacy format (still supported) 9await createCli(defineCommand({}));
2 Run the following:
1bun add -D @reliverse/dler 2bun dler rempts --init my-cmd-1 # or: dler rempts --init my-cmd-1 my-cmd-2 --main src/mod.ts 3# * `--main` is optional, default is `./src/mod.ts` 4# * you can specify multiple commands at once
3 Visit src/app/my-cmd-1/mod.ts
and edit it:
1export default defineCommand({ 2 run() { console.log("Hello, world!"); }, 3});
4. Test it:
1bun src/mod.ts
1import { defineCommand, createCli } from "@reliverse/rempts"; 2 3const main = defineCommand({ 4 meta: { 5 name: "mycli", 6 }, 7 run() { 8 console.log("Happy, Reliversing!"); 9 }, 10}); 11 12// New object format (recommended) 13await createCli({ 14 mainCommand: main, 15}); 16 17// Legacy format (still supported) 18await createCli(main);
1import { relinka } from "@reliverse/relinka"; 2 3import { 4 startPrompt, 5 inputPrompt, 6 selectPrompt, 7 defineCommand, 8 createCli 9} from "@reliverse/rempts"; 10 11const main = defineCommand({ 12 meta: { 13 name: "mycli", 14 version: "1.0.0", 15 description: "CLI powered by Rempts", 16 }, 17 args: { 18 name: { 19 type: "string", 20 required: true, 21 description: "The name of the project", 22 }, 23 }, 24 async run({ args }) { 25 await startPrompt({ 26 title: "Project Setup", 27 }); 28 29 const name = await inputPrompt({ 30 title: "What's your project name?", 31 placeholder: args.name, 32 }); 33 34 const framework = await selectPrompt({ 35 title: "Pick your framework", 36 options: [ 37 { value: "next", label: "Next.js" }, 38 { value: "svelte", label: "SvelteKit" }, 39 { value: "start", label: "TanStack Start" }, 40 ], 41 }); 42 43 relinka("log", "You have selected:", { name, framework }); 44 }, 45}); 46 47// New object format (recommended) 48await createCli({ 49 mainCommand: main, 50}); 51 52// Legacy format (still supported) 53await createCli(main);
1import { relinka } from "@reliverse/relinka";
2
3import {
4 startPrompt,
5 inputPrompt,
6 selectPrompt,
7 defineCommand,
8 runMain,
9} from "@reliverse/rempts";
10
11/**
12 * Main command defined using `defineCommand()`.
13 *
14 * This command demonstrates the full range of launcher features along with all supported argument types:
15 *
16 * - Global Usage Handling: Automatically processes `--help` and `--version`.
17 * - File-Based Commands: Scans "app" for commands (e.g., `init`).
18 * - Comprehensive Argument Parsing: Supports positional, boolean, string, number, and array arguments.
19 * - Interactive Prompts: Uses built-in prompt functions for an engaging CLI experience.
20 */
21const mainCommand = defineCommand({
22 meta: {
23 name: "rempts",
24 version: "1.6.0",
25 description:
26 "An example CLI that supports file-based commands and all argument types.",
27 },
28 args: {
29 // Positional arguments
30 inputFile: {
31 type: "positional",
32 description: "Path to the input file (only for the main command).",
33 },
34 config: {
35 type: "positional",
36 description: "Path to the configuration file.",
37 },
38 // Boolean arguments
39 verbose: {
40 type: "boolean",
41 default: false,
42 description: "Whether to print verbose logs in the main command.",
43 },
44 debug: {
45 type: "boolean",
46 default: false,
47 description: "Enable debug mode for additional logging.",
48 },
49 // String argument
50 name: {
51 type: "string",
52 description: "The name of the project.",
53 },
54 // Number argument
55 timeout: {
56 type: "number",
57 default: 30,
58 description: "Timeout in seconds for the CLI operation.",
59 },
60 // Array argument
61 tags: {
62 type: "array",
63 default: ["cli", "rempts"],
64 description: "List of tags associated with the project.",
65 },
66 },
67 async run({ args, raw }) {
68 // Display invocation details and parsed arguments.
69 relinka("log", "Main command was invoked!");
70 relinka("log", "Parsed main-command args:", args);
71 relinka("log", "Raw argv:", raw);
72 relinka("log", "\nHelp: `rempts --help`, `rempts cmdName --help`");
73
74 // Begin interactive session with a prompt.
75 await startPrompt({
76 title: "Project Setup",
77 });
78
79 // Ask for the project name, falling back to provided argument or a default.
80 const projectName = await inputPrompt({
81 title: "What's your project name?",
82 placeholder: args.name ?? "my-cool-cli",
83 });
84
85 // Let the user pick a framework from a select prompt.
86 const framework = await selectPrompt({
87 title: "Pick your framework",
88 options: [
89 { value: "next", label: "Next.js" },
90 { value: "svelte", label: "SvelteKit" },
91 { value: "start", label: "TanStack Start" },
92 ],
93 });
94
95 // Log all gathered input details.
96 relinka("log", "You have selected:", {
97 projectName,
98 framework,
99 inputFile: args.inputFile,
100 config: args.config,
101 verbose: args.verbose,
102 debug: args.debug,
103 timeout: args.timeout,
104 tags: args.tags,
105 });
106 },
107});
108
109/**
110 * The `createCli()` function sets up the launcher with several advanced features:
111 *
112 * - File-Based Commands: Enables scanning for commands within the "app" directory.
113 * - Alias Mapping: Shorthand flags (e.g., `-v`) are mapped to their full names (e.g., `--verbose`).
114 * - Strict Mode & Unknown Flag Warnings: Unknown flags are either warned about or handled via a callback.
115 * - Negated Boolean Support: Allows flags to be negated (e.g., `--no-verbose`).
116 * - Custom Unknown Flag Handler: Provides custom handling for unrecognized flags.
117 */
118// New object format (recommended)
119await createCli({
120 mainCommand: mainCommand,
121 fileBased: {
122 enable: true, // Enables file-based command detection.
123 cmdsRootPath: "app", // Directory to scan for commands.
124 },
125 alias: {
126 v: "verbose", // Maps shorthand flag -v to --verbose.
127 },
128 strict: false, // Do not throw errors for unknown flags.
129 warnOnUnknown: false, // Warn when encountering unknown flags.
130 negatedBoolean: true, // Support for negated booleans (e.g., --no-verbose).
131 // unknown: (flagName) => {
132 // relinka("warn", "Unknown flag encountered:", flagName);
133 // return false;
134 // },
135});
136
137// Legacy format (still supported)
138await createCli(mainCommand, {
139 fileBased: {
140 enable: true, // Enables file-based command detection.
141 cmdsRootPath: "app", // Directory to scan for commands.
142 },
143 alias: {
144 v: "verbose", // Maps shorthand flag -v to --verbose.
145 },
146 strict: false, // Do not throw errors for unknown flags.
147 warnOnUnknown: false, // Warn when encountering unknown flags.
148 negatedBoolean: true, // Support for negated booleans (e.g., --no-verbose).
149 // unknown: (flagName) => {
150 // relinka("warn", "Unknown flag encountered:", flagName);
151 // return false;
152 // },
153});
Finally, a full-featured CLI launcher without the ceremony. @reliverse/rempts
's so called "launcher" is a uniquely powerful and ergonomic CLI toolkit—one that helps you build delightful developer experiences with less code and more confidence. The launcher supports both programmatically defined commands and file-based routing, so you can structure your CLI however you like. It automatically detects and loads commands from your filesystem and provides robust usage and error handling out-of-the-box. The launcher is more than just a command runner—it's a robust, developer-friendly engine with several advanced features and thoughtful design choices:
File-Based & Defined Commands:
Use commands
in your command definition or let the launcher automatically load commands from a specified directory.
Automatic Command Detection:
The launcher scans your specified cmdsRootPath
for command files matching common patterns such as:
arg-cmdName.{ts,js}
cmdName/index.{ts,js}
cmdName/cmdName-mod.{ts,js}
Built-In Flag Handling:
Automatically processes global flags such as:
--help
and -h
to show usage details.--version
and -v
to display version information.--debug
for verbose logging during development.Unified Argument Parsing:
Seamlessly combines positional and named arguments with zero configuration, auto-parsing booleans, strings, numbers, arrays, and even supporting negated flags like --no-flag
.
Customizable Behavior:
Options such as fileBased.enable
, cmdsRootPath
, and autoExit
allow you to tailor the launcher's behavior. For example, you can choose whether the process should exit automatically on error or allow manual error handling.
Error Management & Usage Output:
The launcher provides clear error messages for missing required arguments, invalid types, or command import issues, and it automatically displays usage information for your CLI.
Lifecycle Hooks: You can define optional lifecycle hooks in your main command:
onLauncherInit
and onLauncherExit
(global, called once per CLI process)onCmdInit
and onCmdExit
(per-command, called before/after each command, but NOT for the main run()
handler)Global Hooks:
onLauncherInit
: Called once, before any command/run() is executed.onLauncherExit
: Called once, after all command/run() logic is finished (even if an error occurs).Per-Command Hooks:
onCmdInit
: Called before each command (not for main run()
).onCmdExit
: Called after each command (not for main run()
).This means:
onCmdInit
and onCmdExit
will be called for each command invocation, not just once for the whole CLI process.run()
handler (and no command is invoked), these hooks are not called; use the run()
handler itself or the global hooks for such logic.onLauncherInit
and onLauncherExit
.Example:
1const main = defineCommand({ 2 onLauncherInit() { relinka('info', 'Global setup (once per process)'); }, 3 onLauncherExit() { relinka('info', 'Global cleanup (once per process)'); }, 4 onCmdInit() { relinka('info', 'Setup for each command'); }, 5 onCmdExit() { relinka('info', 'Cleanup for each command'); }, 6 commands: { ... }, 7 run() { relinka('info', 'Main run handler (no command)'); }, 8}); 9// onLauncherInit/onLauncherExit are called once per process 10// onCmdInit/onCmdExit are called for every command (not for main run()) 11// If you want per-run() logic, use the run() handler or global hooks
Deprecation Notice
setup
and cleanup
names are still supported as aliases for per-command hooks, but will be removed in a future major version. Prefer onCmdInit
and onCmdExit
going forward.subCommands
property is deprecated as well. Please use commands
instead. subCommands
will be removed in a future major version.Dynamic Usage Examples:
File-Based & Programmatic Commands:
Context-Aware Help Output:
Error Handling:
Unified Argument Parsing:
--no-flag
) are supported out of the box.Extensible & Flexible:
autoExit
, cmdsRootPath
, and more.Bun & Node.js Support:
Prompt-First, Modern UX:
For larger CLIs or when you want to programmatically run commands (e.g.: prompt demo, tests, etc), you can organize your commands in a cmds.ts
file and use the runCmd
utility. Example:
1// example/launcher/app/runcmd/cmd.ts 2 3import { relinka } from "@reliverse/relinka"; 4import { defineArgs, defineCommand, runCmd } from "@reliverse/rempts"; 5import { cmdMinimal } from "../cmds.js"; 6 7export default defineCommand({ 8 meta: { 9 name: "runcmd", 10 description: 11 "Demonstrate how to use runCmd() to invoke another command programmatically.", 12 }, 13 args: defineArgs({ 14 name: { 15 type: "string", 16 description: "your name", 17 }, 18 }), 19 async run({ args }) { 20 // const username = args.name ?? "Alice"; 21 const username = args.name; // intentionally missing fallback 22 relinka( 23 "info", 24 `Running the 'minimal' command using runCmd() with name='${username}'`, 25 ); 26 await runCmd(await cmdMinimal(), ["--name", username]); 27 relinka("log", "Done running 'minimal' via runCmd()."); 28 }, 29});
runCmd
with Flexible Argument HandlingThe runCmd
function supports flexible argument passing, automatically normalizing template literals and space-separated strings:
1import { runCmd } from "@reliverse/rempts";
2
3// Traditional way - each argument as separate array element
4await runCmd(cmd, ["--dev", "true", "--name", "John"]);
5
6// Template literals work automatically
7await runCmd(cmd, [`--dev ${isDev}`]); // Automatically converted to ["--dev", "true"]
8await runCmd(cmd, [`--dev ${isDev} --build mod.ts`]); // ["--dev", "true", "--build", "mod.ts"]
9
10// Mixed arrays with template literals and regular strings
11await runCmd(cmd, [
12 `--dev ${isDev} --build mod.ts`,
13 "--pub true",
14 "--someBoolean",
15]);
16
17// Multiple template literals
18await runCmd(cmd, [`--dev ${isDev}`, `--name ${userName}`, `--count ${count}`]);
Remember:
await runCmd(cmd, ['--name "John Doe"']);
"John"
and "Doe"
.loadCommand
The loadCommand
utility helps you load command files from your filesystem. It automatically handles:
./build
and build
work the same)cmd.{ts,js}
files1import { loadCommand } from "@reliverse/rempts"; 2 3// These are equivalent: 4const cmd1 = await loadCommand("./build"); // Looks for build/cmd.ts or build/cmd.js 5const cmd2 = await loadCommand("build"); // Same as above 6const cmd3 = await loadCommand("./build/cmd"); // Explicit path to cmd file 7 8// You can then use the loaded command with runCmd: 9await runCmd(cmd1, ["--some-flag"]);
1// src/app/cmds.ts 2export const getBuildCmd = async (): Promise<Command> => loadCommand("./build"); 3 4// src/cli.ts 5import { runCmd } from "@reliverse/rempts"; 6import { getBuildCmd } from "./app/cmds"; 7await runCmd(await getBuildCmd(), ["--prod"]);
Error Handling: If the command file is not found, you'll get a clear error message:
1No command file found in /path/to/build. Expected to find either: 2 - /path/to/build/cmd.ts 3 - /path/to/build/cmd.js 4Please ensure one of these files exists and exports a default command.
Best Practices:
loadCommand
when you need to load commands from the filesystemrunCmd
to execute the loaded command with argumentssrc/app/yourCmdName/cmd.ts
)src/app/cmds.ts
for better organization1// example/launcher/app/cmds.ts 2import { loadCommand } from "@reliverse/rempts"; 3 4export async function getBuildCmd() { 5 return loadCommand("./build"); 6} 7 8export async function getDeployCmd() { 9 return loadCommand("./deploy"); 10} 11 12// Usage: 13import { getBuildCmd } from "./cmds"; 14const buildCmd = await getBuildCmd(); 15await runCmd(buildCmd, ["--prod"]);
1// example/launcher/app/minimal/cmd.ts 2 3import { relinka } from "@reliverse/relinka"; 4import { defineArgs, defineCommand } from "@reliverse/rempts"; 5 6export default defineCommand({ 7 meta: { 8 name: "minimal", 9 description: "hello world", 10 }, 11 args: defineArgs({ 12 name: { 13 type: "string", 14 description: "your name", 15 required: true, 16 }, 17 }), 18 run({ args }) { 19 relinka("success", `👋 Hello, ${args.name}!`); 20 }, 21});
runCmdWithSubcommands
for Subcommands and Nested SubcommandsIf you need to programmatically run commands that support subcommands (including nested subcommands), use runCmdWithSubcommands
:
1import { runCmdWithSubcommands } from "@reliverse/rempts";
2
3// Single-level subcommand
4await runCmdWithSubcommands(mainCmd, [`build --input src/mod.ts --someBoolean`]);
5
6// Subcommand with positional arguments
7await runCmdWithSubcommands(mainCmd, [`build src/mod.ts --someBoolean`]);
8
9// Nested subcommands
10await runCmdWithSubcommands(mainCmd, [`build someSubCmd src/mod.ts --no-cjs`]);
11await runCmdWithSubcommands(mainCmd, [`build sub1 sub2 sub3 file.ts --flag`]);
12
13// Mixed array with subcommands
14await runCmdWithSubcommands(mainCmd, [
15 `build someSubCmd src/mod.ts`,
16 "--no-cjs",
17 "--verbose"
18]);
Note:
runCmdWithSubcommands
automatically normalizes template literals and space-separated strings, just like runCmd
.await runCmdWithSubcommands(cmd, ['--name "John Doe"']);
runCmdWithSubcommands
for the most robust behavior.Below is a demonstration of how to define and use all supported argument types in rempts: positional, boolean, string, number, and array. This includes example CLI invocations and the resulting parsed output.
1import { defineCommand, createCli } from "@reliverse/rempts"; 2 3const main = defineCommand({ 4 meta: { 5 name: "mycli", 6 version: "1.0.0", 7 description: "Demo of all argument types", 8 }, 9 args: { 10 // Positional argument (required) 11 input: { 12 type: "positional", 13 required: true, 14 description: "Input file path", 15 }, 16 // Boolean flag (default: false) 17 verbose: { 18 type: "boolean", 19 default: false, 20 description: "Enable verbose output", 21 }, 22 // String option (optional) 23 name: { 24 type: "string", 25 description: "Your name", 26 }, 27 // Number option (optional, with default) 28 count: { 29 type: "number", 30 default: 1, 31 description: "How many times to run", 32 }, 33 // Array option (can be repeated, accepts any value) 34 tags: { 35 type: "array", 36 default: ["demo"], 37 description: "Tags for this run (repeatable)", 38 }, 39 }, 40 run({ args }) { 41 console.log("Parsed args:", args); 42 }, 43}); 44 45// New object format (recommended) 46await createCli({ 47 mainCommand: main, 48}); 49 50// Legacy format (still supported) 51await createCli(main);
1mycli input.txt 2# → args.input = "input.txt"
1mycli input.txt --verbose 2# → args.verbose = true 3mycli input.txt --no-verbose 4# → args.verbose = false
1mycli input.txt --name Alice 2# → args.name = "Alice" 3mycli input.txt 4# → args.name = undefined
1mycli input.txt --count 5 2# → args.count = 5 3mycli input.txt 4# → args.count = 1 (default)
You can provide array values using any of the following syntaxes (mix and match as needed):
Repeated flags:
1mycli input.txt --tags foo --tags bar --tags baz 2# → args.tags = ["foo", "bar", "baz"]
Comma-separated values (with or without spaces):
1mycli input.txt --tags foo,bar,baz 2mycli input.txt --tags foo, bar, baz 3# → args.tags = ["foo", "bar", "baz"]
Bracketed values (must be passed as a single argument!):
1mycli input.txt --tags "[foo,bar,baz]" 2# → args.tags = ["foo", "bar", "baz"]
Mix and match:
1mycli input.txt --tags foo --tags "[bar,bar2,bar3]" --tags baz 2# → args.tags = ["foo", "bar", "bar2", "bar3", "baz"]
Important:
- Quoted values (single or double quotes around elements) are NOT supported and will throw an error.
- Example:
--tags 'foo'
or--tags "[\"bar\",'baz']"
will throw an error.- Bracketed or comma-separated lists must be passed as a single argument.
- Example:
--tags "[foo,bar]"
(quotes around the whole value, not around elements)- If you split a bracketed value across arguments, you will get a warning or incorrect parsing.
- Shells remove quotes before passing arguments to the CLI. If you want to pass a value with commas or brackets, always quote the whole value.
- Troubleshooting:
- If you see a warning about possible shell splitting, try quoting the whole value:
--tags "[a,b,c]"
- If you see an error about quoted values, remove quotes around individual elements.
Example error:
1$ bun example/launcher/modern.ts build --entry "[foo.ts," "bar.ts]" 2✖ Don't use quotes around array elements. 3✖ Also — don't use spaces — unless you wrap the whole array in quotes. 4⚠ Array argument --entry: Detected possible shell splitting of bracketed value ('[foo.ts,'). 5⚠ If you intended to pass a bracketed list, quote the whole value like: --entry "[a, b, c]"
1mycli input.txt --verbose --name Alice --count 3 --tags foo --tags bar 2# → args = { 3# input: "input.txt", 4# verbose: true, 5# name: "Alice", 6# count: 3, 7# tags: ["foo", "bar"] 8# }
allowed
All argument types support an optional allowed
property that restricts which values can be passed:
1const main = defineCommand({ 2 args: { 3 // Only allow specific string values 4 mode: { 5 type: "string", 6 allowed: ["development", "production", "test"], 7 description: "The mode to run in" 8 }, 9 10 // Only allow specific boolean values (e.g. if you only want true) 11 force: { 12 type: "boolean", 13 allowed: [true], 14 description: "Force the operation" 15 }, 16 17 // Only allow specific numbers 18 level: { 19 type: "number", 20 allowed: [1, 2, 3], 21 description: "The level to use" 22 }, 23 24 // Only allow specific values in an array 25 tags: { 26 type: "array", 27 allowed: ["web", "api", "mobile"], 28 description: "Tags to apply" 29 }, 30 31 // Only allow specific positional values 32 action: { 33 type: "positional", 34 allowed: ["build", "serve", "test"], 35 description: "The action to perform" 36 } 37 } 38});
If someone tries to pass a value that's not in the allowed
list, they'll get a helpful error message:
1mycli --mode staging 2# Error: Invalid value for --mode: staging. Allowed values are: development, production, test 3 4mycli --level 4 5# Error: Invalid value for --level: 4. Allowed values are: 1, 2, 3 6 7mycli --tags desktop 8# Error: Invalid value in array --tags: desktop. Allowed values are: web, api, mobile
The validation happens after type casting, so for example with numbers, the input will first be converted to a number and then checked against the allowed list.
Bug report? Prompt idea? Want to build the best DX possible?
You're in the right place! Please help us make the best CLI toolkit possible.
TypeScript Support:
All APIs are fully typed. See src/types.ts
for advanced customization and type inference.
Examples:
example/launcher/classic.ts
example/launcher/modern.ts
example/prompts/mod.ts
Components and Utilities:
dler libs
in the future (all main components will be published as separate packages; @reliverse/rempts
will be a wrapper for all of them)bun:test
@reliverse/cli
– CLI-first toolkit for fullstack workflows@reliverse/reliarg
– Tiny, strict, zero-dep argument parser with value validation support (allowed
property for restricting argument values)@reliverse/reglob
– Fast, minimal file matcher@reliverse/relinka
– Styled CLI logs, steps, and symbolsBug report? Prompt idea? Want to build the best DX possible?
You're in the right place:
No classes. No magic. Just clean, composable tools for CLI devs.
💖 MIT (see LICENSE and LICENCES) © blefnk (Nazar Kornienko)
No vulnerabilities found.
No security vulnerabilities found.