@reliverse/rempts 1.7.0 β†’ 1.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,98 +2,148 @@
2
2
 
3
3
  > @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.
4
4
 
5
- [πŸ’¬ Discord](https://discord.gg/3GawfWfAPe) β€” [πŸ“¦ NPM](https://npmjs.com/package/@reliverse/rempts) β€” [🧠 Docs](https://docs.reliverse.org/reliverse/rempts) β€” [🌐 JSR](https://jsr.io/@reliverse/rempts) β€” [✨ GitHub](https://github.com/reliverse/rempts)
5
+ [sponsor](https://github.com/sponsors/blefnk) β€” [discord](https://discord.gg/Pb8uKbwpsJ) β€” [repo](https://github.com/reliverse/rempts) β€” [npm](https://npmjs.com/@reliverse/rempts)
6
6
 
7
- ## Stop Fighting Your CLI
7
+ ## Features
8
8
 
9
- - CLIs are still the sharpest tool in the builder's belt. But most libraries are either clunky or crusty.
10
- - **@reliverse/rempts** is your end-to-end CLI UI + command framework β€” made for developer experience, DX precision, and high-context terminal UX.
11
- - No more hacking together `inquirer`, `citty`, `commander`, `chalk`, and other friends.
12
-
13
- ## What Makes It Different?
14
-
15
- - πŸ“‚ File-based commands (optional)
9
+ - πŸ«‚ Rempts prevents you from fighting with your CLI tool
10
+ - ✨ Rempts is your end-to-end CLI UI + command framework
11
+ - πŸ’ͺ Made for DX precision and high-context terminal UX
12
+ - πŸ“‚ File-based commands (app router style by default)
13
+ - 🏎️ Prompt engine that *feels* modern, actually is
16
14
  - 🧠 Type-safe from args to prompts
17
- - 🎨 Customizable themes, styled output
18
- - 🧩 Router + argument parser built-in
19
15
  - ⚑ Blazing-fast, no runtime baggage
16
+ - 🧩 Router + argument parser built-in
17
+ - 🎨 Customizable themes, styled output
18
+ - 🚨 Crash-safe (Ctrl+C, SIGINT, errors)
20
19
  - πŸͺ„ Minimal API surface, max expressiveness
21
- - 🏎️ Prompt engine that *feels* modern, actually is
22
20
  - πŸ§ͺ Scriptable for testing, stable for production
23
- - 🚨 Crash-safe (Ctrl+C, SIGINT, errors)
24
- - πŸ†• Automatic commands creation via `rempts init --cmd my-cool-cmd`
21
+ - πŸ†• Automatic commands creation (via `dler init --cmd my-cool-cmd`)
22
+ - 🏞️ No more hacking together `inquirer`, `citty`, `commander`, `chalk`
23
+
24
+ ## Installation
25
+
26
+ ```bash
27
+ bun add @reliverse/rempts
28
+ ```
29
+
30
+ **Coming soon**:
31
+
32
+ ```bash
33
+ bun i -g @reliverse/dler
34
+ dler rempts init --cmd my-cmd-1
35
+ dler rempts init --cmds
36
+ ```
37
+
38
+ ## Usage Examples
39
+
40
+ - [Prompts](#prompts)
41
+ - [Launcher](#launcher)
25
42
 
26
43
  ## Screenshot
27
44
 
28
- ![Rempts Example CLI Screenshot](./example.png)
45
+ ![Rempts Example CLI Screenshot](./example/example.png)
29
46
 
30
- ## Terminology
47
+ ## API Overview
31
48
 
32
- - **Command/Subcommand**: A command is a function that defines the behavior of a CLI.
33
- - **Argument**: An argument is a value that is passed to a command.
34
- - **Flag**: A flag is a boolean argument that is used to enable or disable a feature.
35
- - **Option**: An option is a named argument that is used to configure a command.
49
+ All main prompts APIs are available from the package root:
36
50
 
37
- ## CLI Launcher (Router)
51
+ ```ts
52
+ import {
53
+ // ...prompts
54
+ defineCommand, runMain, defineArgs,
55
+ inputPrompt, selectPrompt, multiselectPrompt, numberPrompt,
56
+ confirmPrompt, togglePrompt, spinnerTaskPrompt, progressTaskPrompt,
57
+ startPrompt, endPrompt, resultPrompt, nextStepsPrompt,
58
+ // ...hooks
59
+ useSpinner,
60
+ // ...launcher
61
+ runMain, defineCommand, defineArgs,
62
+ // ...types
63
+ // ...more
64
+ } from "@reliverse/rempts";
65
+ ```
38
66
 
39
- 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 subcommands and file-based routing, so you can structure your CLI however you like. It automatically detects and loads subcommands 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:
67
+ > See [`src/mod.ts`](./src/mod.ts) for the full list of exports.
40
68
 
41
- - **File-Based & Defined Subcommands:**
42
- Use `subCommands` in your command definition or let the launcher automatically load commands from a specified directory.
69
+ ## Prompts
43
70
 
44
- - **Automatic Command Detection:**
45
- The launcher scans your specified `cmdsRootPath` for command files matching common patterns such as:
46
- - `arg-cmdName.{ts,js}`
47
- - `cmdName/index.{ts,js}`
48
- - `cmdName/cmdName-mod.{ts,js}`
49
- - And more β€” with automatic usage output if a command file is not found.
71
+ ### Built-in Prompts
50
72
 
51
- - **Built-In Flag Handling:**
52
- Automatically processes global flags such as:
53
- - `--help` and `-h` to show usage details.
54
- - `--version` and `-v` to display version information.
55
- - `--debug` for verbose logging during development.
73
+ | Prompt | Description |
74
+ |---------------------------|-----------------------------------------------------------|
75
+ | `inputPrompt` | Single-line input (with mask support, e.g. for passwords) |
76
+ | `selectPrompt` | Single-choice radio menu |
77
+ | `multiselectPrompt` | Multi-choice checkbox menu |
78
+ | `numberPrompt` | Type-safe number input |
79
+ | `confirmPrompt` | Yes/No toggle |
80
+ | `togglePrompt` | Custom on/off toggles |
81
+ | `progressTaskPrompt` | Progress bar for async tasks |
82
+ | `resultPrompt` | Show results in a styled box |
83
+ | `nextStepsPrompt` | Show next steps in a styled list |
84
+ | `startPrompt`/`endPrompt` | Makes CLI start/end flows look nice |
85
+ | `spinnerTaskPrompt` | Async loader with spinner (possibly will be deprecated) |
86
+ | `datePrompt` | Date input with format validation |
87
+ | `anykeyPrompt` | Wait for any keypress |
56
88
 
57
- - **Unified Argument Parsing:**
58
- Seamlessly combines positional and named arguments with zero configuration, auto-parsing booleans, strings, numbers, arrays, and even supporting negated flags like `--no-flag`.
89
+ ### Hooks
59
90
 
60
- - **Customizable Behavior:**
61
- Options such as `fileBasedCmds.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.
91
+ | Hook | Description |
92
+ |--------------|--------------------|
93
+ | `useSpinner` | Start/stop spinner |
62
94
 
63
- - **Error Management & Usage Output:**
64
- 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.
95
+ ### Notices
65
96
 
66
- - **Lifecycle Hooks:**
67
- - You can define optional `setup` and `cleanup` functions in your main command. These hooks are called automatically before and after any command or subcommand runs (including file-based and programmatic subcommands). This is perfect for initializing resources or cleaning up after execution.
97
+ - `setup`/`cleanup` are now `onCmdStart`/`onCmdEnd` (old names still work for now).
68
98
 
69
- - **Dynamic Usage Examples:**
70
- - The launcher inspects your available subcommands and their argument definitions, then prints a plausible example CLI invocation for a random subcommand directly in the help output. This helps users understand real-world usage at a glance.
99
+ ### Prompts Usage Example
71
100
 
72
- - **File-Based & Programmatic Subcommands:**
73
- - Both file-based and object subcommands are fully supported. The launcher can introspect their argument definitions and metadata for help, usage, and validation.
74
- - File-based subcommands are auto-discovered from your filesystem, while programmatic subcommands can be defined inline in your main command.
101
+ ```ts
102
+ import { relinka } from "@reliverse/relinka";
75
103
 
76
- - **Context-Aware Help Output:**
77
- - The help/usage output adapts to your CLI's structure, showing available subcommands, their aliases, argument details, and even dynamic usage examples. It also displays global options and context-specific error messages.
104
+ import {
105
+ startPrompt,
106
+ inputPrompt,
107
+ selectPrompt,
108
+ defineCommand,
109
+ runMain
110
+ } from "@reliverse/rempts";
78
111
 
79
- - **Error Handling:**
80
- - The launcher provides clear, actionable error messages for missing required arguments, invalid types, unknown commands, and import errors. It always shows relevant usage information to help users recover quickly.
112
+ async function main() {
113
+ await startPrompt({ title: "Project Setup" });
81
114
 
82
- - **Unified Argument Parsing:**
83
- - All arguments (positional, named, boolean, string, number, array) are parsed and validated automatically. Negated flags (like `--no-flag`) are supported out of the box.
115
+ const name = await inputPrompt({
116
+ title: "What's your project name?",
117
+ defaultValue: "my-cool-project",
118
+ });
84
119
 
85
- - **Extensible & Flexible:**
86
- - The launcher is highly extensible. You can use it with both Bun and Node.js, and it works seamlessly with both file-based and programmatic command definitions. You can also customize its behavior with options like `autoExit`, `cmdsRootPath`, and more.
120
+ const framework = await selectPrompt({
121
+ title: "Pick your framework",
122
+ options: [
123
+ { value: "next", label: "Next.js" },
124
+ { value: "svelte", label: "SvelteKit" },
125
+ { value: "start", label: "TanStack Start" },
126
+ ],
127
+ defaultValue: "next",
128
+ });
87
129
 
88
- - **Bun & Node.js Support:**
89
- - The launcher is designed to work in both Bun and Node.js environments, so you can use it in any modern JavaScript/TypeScript project.
130
+ console.log("Your result:", { name, framework });
131
+ };
90
132
 
91
- - **Prompt-First, Modern UX:**
92
- - The launcher integrates tightly with the prompt engine, so you can build interactive, delightful CLIs with minimal effort.
133
+ await main();
134
+ ```
135
+
136
+ ## Launcher
93
137
 
94
- ### Launcher Usage Example
138
+ ### Terminology
95
139
 
96
- ‼️ Go to [Usage Examples](#usage-examples) section for a more detailed example.
140
+ - **Launcher/Router**: The main entry point for your CLI. Visit [CLI Launcher (Router)](#cli-launcher-router) section to learn more.
141
+ - **Command**: A command is a function that defines the inner script launched by the main script where runMain() is used or by some other command.
142
+ - **Argument**: An argument is a value that is passed to a command.
143
+ - **Flag**: A flag is a boolean argument that is used to enable or disable a feature.
144
+ - **Option**: An option is a named argument that is used to configure a command.
145
+
146
+ #### Launcher Usage Example
97
147
 
98
148
  ```ts
99
149
  import { relinka } from "@reliverse/relinka";
@@ -106,13 +156,13 @@ const main = defineCommand({
106
156
  version: "1.0.0",
107
157
  description: "Rempts Launcher Playground CLI",
108
158
  },
109
- setup() {
159
+ onCmdStart() {
110
160
  relinka("success", "Setup");
111
161
  },
112
- cleanup() {
162
+ onCmdEnd() {
113
163
  relinka("success", "Cleanup");
114
164
  },
115
- subCommands: {
165
+ commands: {
116
166
  build: () => import("./app/build/cmd.js").then((r) => r.default),
117
167
  deploy: () => import("./app/deploy/cmd.js").then((r) => r.default),
118
168
  debug: () => import("./app/debug/cmd.js").then((r) => r.default),
@@ -126,16 +176,16 @@ await runMain(main);
126
176
  await runMain(myCommand, {
127
177
  fileBasedCmds: {
128
178
  enable: true,
129
- cmdsRootPath: "./cli/args", // default is `./app`
179
+ cmdsRootPath: "my-cmds", // default is `./app`
130
180
  },
131
181
  // Optionally disable auto-exit to handle errors manually:
132
182
  autoExit: false,
133
183
  });
134
184
  ```
135
185
 
136
- This flexibility allows you to easily build a rich, multi-command CLI with minimal boilerplate. The launcher even supports nested subcommands, making it simple to construct complex CLI applications.
186
+ 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.
137
187
 
138
- ### File-Based Subcommands
188
+ #### File-Based Commands
139
189
 
140
190
  Drop a `./src/cli/app/add/index.ts` and it's live.
141
191
 
@@ -155,7 +205,7 @@ export default defineCommand({
155
205
  }),
156
206
  },
157
207
  async run({ args }) {
158
- relinka("info", "Adding:", args.name);
208
+ relinka("log", "Adding:", args.name);
159
209
  },
160
210
  });
161
211
  ```
@@ -169,22 +219,10 @@ export default defineCommand({
169
219
 
170
220
  **Hint**:
171
221
 
172
- - Install `bun i -g @reliverse/rempts-cli`
173
- - Use `rempts init --cmd my-cmd-1 my-cmd-2` to init commands automatically
174
-
175
- ## πŸ“¦ Built-In Prompts
176
-
177
- - 🧠 `inputPrompt` – Single-line, password, masked
178
- - βœ… `selectPrompt` – Radio menu
179
- - 🧰 `multiselectPrompt` – Checkbox menu
180
- - πŸ”’ `numberPrompt` – Type-safe number input
181
- - πŸ”„ `confirmPrompt` – Yes/No toggle
182
- - πŸš₯ `togglePrompt` – Custom on/off toggles
183
- - ⏳ `spinnerPrompt` – Async loaders with status
184
- - πŸ“œ `logPrompt` – Styled logs / steps
185
- - 🧼 `clearPrompt` – Clears console with style
222
+ - Install `bun add -D @reliverse/dler`
223
+ - Use `dler init --cmd cmd1 cmd2` to init commands for rempts launcher's automatically
186
224
 
187
- ## 🧱 Minimal, Functional API
225
+ ### Advanced Minimal API
188
226
 
189
227
  ```ts
190
228
  defineCommand({
@@ -195,7 +233,7 @@ defineCommand({
195
233
  animals: { type: "array", options: ["cat","dog"] },
196
234
  },
197
235
  async run({ args, raw }) { // or `async run(ctx)`
198
- relinka("info", args.name, args.verbose, args.animals); // or `relinka("info", ctx.args.name, ...);`
236
+ relinka("log", args.name, args.verbose, args.animals); // or `relinka("log", ctx.args.name, ...);`
199
237
  },
200
238
  });
201
239
  ```
@@ -207,14 +245,14 @@ defineCommand({
207
245
  - Default values, validations, descriptions
208
246
  - Full help rendering from metadata
209
247
 
210
- ## Theming + Customization
248
+ ### Theming + Customization
211
249
 
212
250
  - Built-in output formatter and logger
213
251
  - Override styles via prompt options
214
252
  - Smart layout for small terminals
215
253
  - Looks great in plain scripts or full CLI apps
216
254
 
217
- ## Playground
255
+ ### Playground
218
256
 
219
257
  ```bash
220
258
  bun i -g @reliverse/rempts-cli
@@ -233,9 +271,9 @@ bun dev # supported options: name
233
271
  - Both `rempts examples` from @reliverse/rempts and `bun dev` (which is the same thing) are themselves examples of `launcher` functionality.
234
272
  - This launcher will show you a `multiselectPrompt()` where you can choose which CLI prompts you want to play with.
235
273
 
236
- ## Usage Examples
274
+ ### Launcher Usage Examples
237
275
 
238
- ### Minimal Usage Example
276
+ #### Minimal Usage Example
239
277
 
240
278
  **1 Create a `src/mod.ts` file:**
241
279
 
@@ -262,7 +300,13 @@ export default defineCommand({
262
300
  });
263
301
  ```
264
302
 
265
- ### Medium Usage Example
303
+ **4. Test it:**
304
+
305
+ ```bash
306
+ bun src/mod.ts
307
+ ```
308
+
309
+ #### Medium Usage Example
266
310
 
267
311
  ```ts
268
312
  import { defineCommand, runMain } from "@reliverse/rempts";
@@ -279,7 +323,7 @@ const main = defineCommand({
279
323
  await runMain(main);
280
324
  ```
281
325
 
282
- ### Classic Usage Example
326
+ #### Classic Usage Example
283
327
 
284
328
  ```ts
285
329
  import { relinka } from "@reliverse/relinka";
@@ -307,7 +351,7 @@ const main = defineCommand({
307
351
  },
308
352
  async run({ args }) {
309
353
  await startPrompt({
310
- title: "πŸš€ Project Setup",
354
+ title: "Project Setup",
311
355
  });
312
356
 
313
357
  const name = await inputPrompt({
@@ -324,14 +368,14 @@ const main = defineCommand({
324
368
  ],
325
369
  });
326
370
 
327
- relinka("info", "You have selected:", { name, framework });
371
+ relinka("log", "You have selected:", { name, framework });
328
372
  },
329
373
  });
330
374
 
331
375
  await runMain(main);
332
376
  ```
333
377
 
334
- ### Advanced Usage Example
378
+ #### Advanced Usage Example
335
379
 
336
380
  ```ts
337
381
  import { relinka } from "@reliverse/relinka";
@@ -350,7 +394,7 @@ import {
350
394
  * This command demonstrates the full range of launcher features along with all supported argument types:
351
395
  *
352
396
  * - Global Usage Handling: Automatically processes `--help` and `--version`.
353
- * - File-Based Subcommands: Scans "app" for subcommands (e.g., `init`).
397
+ * - File-Based Commands: Scans "app" for commands (e.g., `init`).
354
398
  * - Comprehensive Argument Parsing: Supports positional, boolean, string, number, and array arguments.
355
399
  * - Interactive Prompts: Uses built-in prompt functions for an engaging CLI experience.
356
400
  */
@@ -359,7 +403,7 @@ const mainCommand = defineCommand({
359
403
  name: "rempts",
360
404
  version: "1.6.0",
361
405
  description:
362
- "An example CLI that supports file-based subcommands and all argument types.",
406
+ "An example CLI that supports file-based commands and all argument types.",
363
407
  },
364
408
  args: {
365
409
  // Positional arguments
@@ -402,14 +446,14 @@ const mainCommand = defineCommand({
402
446
  },
403
447
  async run({ args, raw }) {
404
448
  // Display invocation details and parsed arguments.
405
- relinka("info", "Main command was invoked!");
406
- relinka("info", "Parsed main-command args:", args);
407
- relinka("info", "Raw argv:", raw);
408
- relinka("info", "\nHelp: `rempts --help`, `rempts cmdName --help`");
449
+ relinka("log", "Main command was invoked!");
450
+ relinka("log", "Parsed main-command args:", args);
451
+ relinka("log", "Raw argv:", raw);
452
+ relinka("log", "\nHelp: `rempts --help`, `rempts cmdName --help`");
409
453
 
410
454
  // Begin interactive session with a prompt.
411
455
  await startPrompt({
412
- title: "πŸš€ Project Setup",
456
+ title: "Project Setup",
413
457
  });
414
458
 
415
459
  // Ask for the project name, falling back to provided argument or a default.
@@ -429,7 +473,7 @@ const mainCommand = defineCommand({
429
473
  });
430
474
 
431
475
  // Log all gathered input details.
432
- relinka("info", "You have selected:", {
476
+ relinka("log", "You have selected:", {
433
477
  projectName,
434
478
  framework,
435
479
  inputFile: args.inputFile,
@@ -445,7 +489,7 @@ const mainCommand = defineCommand({
445
489
  /**
446
490
  * The `runMain()` function sets up the launcher with several advanced features:
447
491
  *
448
- * - File-Based Subcommands: Enables scanning for subcommands within the "app" directory.
492
+ * - File-Based Commands: Enables scanning for commands within the "app" directory.
449
493
  * - Alias Mapping: Shorthand flags (e.g., `-v`) are mapped to their full names (e.g., `--verbose`).
450
494
  * - Strict Mode & Unknown Flag Warnings: Unknown flags are either warned about or handled via a callback.
451
495
  * - Negated Boolean Support: Allows flags to be negated (e.g., `--no-verbose`).
@@ -453,8 +497,8 @@ const mainCommand = defineCommand({
453
497
  */
454
498
  await runMain(mainCommand, {
455
499
  fileBasedCmds: {
456
- enable: true, // Enables file-based subcommand detection.
457
- cmdsRootPath: "app", // Directory to scan for subcommands.
500
+ enable: true, // Enables file-based command detection.
501
+ cmdsRootPath: "app", // Directory to scan for commands.
458
502
  },
459
503
  alias: {
460
504
  v: "verbose", // Maps shorthand flag -v to --verbose.
@@ -469,6 +513,185 @@ await runMain(mainCommand, {
469
513
  });
470
514
  ```
471
515
 
516
+ ### CLI Launcher (Router)
517
+
518
+ 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:
519
+
520
+ - **File-Based & Defined Commands:**
521
+ Use `commands` in your command definition or let the launcher automatically load commands from a specified directory.
522
+
523
+ - **Automatic Command Detection:**
524
+ The launcher scans your specified `cmdsRootPath` for command files matching common patterns such as:
525
+ - `arg-cmdName.{ts,js}`
526
+ - `cmdName/index.{ts,js}`
527
+ - `cmdName/cmdName-mod.{ts,js}`
528
+ - And more β€” with automatic usage output if a command file is not found.
529
+
530
+ - **Built-In Flag Handling:**
531
+ Automatically processes global flags such as:
532
+ - `--help` and `-h` to show usage details.
533
+ - `--version` and `-v` to display version information.
534
+ - `--debug` for verbose logging during development.
535
+
536
+ - **Unified Argument Parsing:**
537
+ Seamlessly combines positional and named arguments with zero configuration, auto-parsing booleans, strings, numbers, arrays, and even supporting negated flags like `--no-flag`.
538
+
539
+ - **Customizable Behavior:**
540
+ Options such as `fileBasedCmds.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.
541
+
542
+ - **Error Management & Usage Output:**
543
+ 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.
544
+
545
+ - **Lifecycle Hooks:**
546
+ You can define optional lifecycle hooks in your main command:
547
+ - `onLauncherStart` and `onLauncherEnd` (global, called once per CLI process)
548
+ - `onCmdStart` and `onCmdEnd` (per-command, called before/after each command, but NOT for the main `run()` handler)
549
+
550
+ **Global Hooks:**
551
+ - `onLauncherStart`: Called once, before any command/run() is executed.
552
+ - `onLauncherEnd`: Called once, after all command/run() logic is finished (even if an error occurs).
553
+
554
+ **Per-Command Hooks:**
555
+ - `onCmdStart`: Called before each command (not for main `run()`).
556
+ - `onCmdEnd`: Called after each command (not for main `run()`).
557
+
558
+ This means:
559
+ - If your CLI has multiple commands, `onCmdStart` and `onCmdEnd` will be called for each command invocation, not just once for the whole CLI process.
560
+ - If your main command has a `run()` handler (and no command is invoked), these hooks are **not** called; use the `run()` handler itself or the global hooks for such logic.
561
+ - This allows you to perform setup/teardown logic specific to each command execution.
562
+ - If you want logic to run only once for the entire CLI process, use `onLauncherStart` and `onLauncherEnd`.
563
+
564
+ **Example:**
565
+
566
+ ```ts
567
+ const main = defineCommand({
568
+ onLauncherStart() { relinka('info', 'Global setup (once per process)'); },
569
+ onLauncherEnd() { relinka('info', 'Global cleanup (once per process)'); },
570
+ onCmdStart() { relinka('info', 'Setup for each command'); },
571
+ onCmdEnd() { relinka('info', 'Cleanup for each command'); },
572
+ commands: { ... },
573
+ run() { relinka('info', 'Main run handler (no command)'); },
574
+ });
575
+ // onLauncherStart/onLauncherEnd are called once per process
576
+ // onCmdStart/onCmdEnd are called for every command (not for main run())
577
+ // If you want per-run() logic, use the run() handler or global hooks
578
+ ```
579
+
580
+ - **Deprecation Notice**
581
+ - The legacy `setup` and `cleanup` names are still supported as aliases for per-command hooks, but will be removed in a future major version. Prefer `onCmdStart` and `onCmdEnd` going forward.
582
+ - The `subCommands` property is deprecated as well. Please use `commands` instead. `subCommands` will be removed in a future major version.
583
+
584
+ - **Dynamic Usage Examples:**
585
+ - The launcher inspects your available commands and their argument definitions, then prints a plausible example CLI invocation for a random command directly in the help output. This helps users understand real-world usage at a glance.
586
+
587
+ - **File-Based & Programmatic Commands:**
588
+ - Both file-based and object commands are fully supported. The launcher can introspect their argument definitions and metadata for help, usage, and validation.
589
+ - File-based commands are auto-discovered from your filesystem, while programmatic commands can be defined inline in your main command.
590
+
591
+ - **Context-Aware Help Output:**
592
+ - The help/usage output adapts to your CLI's structure, showing available commands, their aliases, argument details, and even dynamic usage examples. It also displays global options and context-specific error messages.
593
+
594
+ - **Error Handling:**
595
+ - The launcher provides clear, actionable error messages for missing required arguments, invalid types, unknown commands, and import errors. It always shows relevant usage information to help users recover quickly.
596
+
597
+ - **Unified Argument Parsing:**
598
+ - All arguments (positional, named, boolean, string, number, array) are parsed and validated automatically. Negated flags (like `--no-flag`) are supported out of the box.
599
+
600
+ - **Extensible & Flexible:**
601
+ - The launcher is highly extensible. You can use it with both Bun and Node.js, and it works seamlessly with both file-based and programmatic command definitions. You can also customize its behavior with options like `autoExit`, `cmdsRootPath`, and more.
602
+
603
+ - **Bun & Node.js Support:**
604
+ - The launcher is designed to work in both Bun and Node.js environments, so you can use it in any modern JavaScript/TypeScript project.
605
+
606
+ - **Prompt-First, Modern UX:**
607
+ - The launcher integrates tightly with the prompt engine, so you can build interactive, delightful CLIs with minimal effort.
608
+
609
+ ### Launcher Programmatic Execution
610
+
611
+ For larger CLIs or when you want to programmatically run commands (e.g.: [prompt demo](./example/prompts/mod.ts), tests, etc), you can organize your commands in a `cmds.ts` file and use the `runCmd` utility.
612
+
613
+ **Pro Tips & Best Practices**:
614
+
615
+ - Install `dler` globally and run `dler rempts init --cmds` to generate a `src/app/cmds.ts` (custom path is supported) file in your project.
616
+ - You can use any name for the `cmds.ts` file and store it anywhere, but `src/app/cmds.ts` is a good convention you can follow.
617
+ - Use the async function pattern for lazy loading if you have many commands or care about startup performance.
618
+ - Use eager loading (const) for small CLIs or demos where simplicity is preferred.
619
+
620
+ **Lazy Loading (Recommended for Most CLIs)**:
621
+
622
+ ```ts
623
+ // example/launcher/app/cmds.ts
624
+
625
+ export async function getCmdHooks() {
626
+ return (await import("./hooks/cmd.js")).default;
627
+ }
628
+
629
+ export async function getCmdFoo() {
630
+ return (await import("./foo/cmd.js")).default;
631
+ }
632
+
633
+ // ...more commands
634
+ ```
635
+
636
+ Usage:
637
+
638
+ ```ts
639
+ // example/prompts/mod.ts
640
+
641
+ import { getCmdHooks } from "@/launcher/app/cmds.js";
642
+ import { runCmd } from "@reliverse/rempts";
643
+
644
+ await runCmd(await getCmdHooks(), ["--flag"]);
645
+ // OR:
646
+ // const hooksCmd = await getCmdHooks();
647
+ // await runCmd(hooksCmd, ["--flag"]);
648
+ ```
649
+
650
+ **Alternative: Eager Loading (All Commands Loaded at Startup)**:
651
+
652
+ ```ts
653
+ // example/launcher/app/cmds.ts
654
+ export const hooksCmd = (await import("./hooks/cmd.js")).default;
655
+ export const fooCmd = (await import("./foo/cmd.js")).default;
656
+ // ...more commands
657
+ ```
658
+
659
+ Usage:
660
+
661
+ ```ts
662
+ import { hooksCmd } from "./cmds.js";
663
+ import { runCmd } from "@reliverse/rempts";
664
+
665
+ await runCmd(hooksCmd, ["--flag"]);
666
+ ```
667
+
668
+ **Programmatic Command Execution with `runCmd`**:
669
+
670
+ The `runCmd` utility lets you run a command's `run()` handler with parsed arguments, outside of the full launcher context. This is useful for demos, tests, or custom flows:
671
+
672
+ ```ts
673
+ import { runCmd } from "@reliverse/rempts";
674
+ import { hooksCmd } from "./cmds.js";
675
+
676
+ await runCmd(hooksCmd, ["--flag"]); // argv as array of strings
677
+ ```
678
+
679
+ Or with lazy loading:
680
+
681
+ ```ts
682
+ const hooksCmd = await getCmdHooks();
683
+ await runCmd(hooksCmd, ["--flag"]);
684
+ ```
685
+
686
+ **Note:** `runCmd` only runs the command's `run()` handler and does not handle subcommands, file-based commands, or global hooks. For full CLI behavior, use `runMain`.
687
+
688
+ **Performance Note:**
689
+
690
+ - Eager loading (`const`) loads all commands at startup, which may impact performance for large CLIs.
691
+ - Lazy loading (`async function`) loads each command only when needed, improving startup time and memory usage.
692
+
693
+ Choose the pattern that best fits your CLI's size and usage!
694
+
472
695
  ## Contributing
473
696
 
474
697
  Bug report? Prompt idea? Want to build the best DX possible?
@@ -481,6 +704,24 @@ You're in the right place:
481
704
 
482
705
  > *No classes. No magic. Just clean, composable tools for CLI devs.*
483
706
 
707
+ ### Notices For Contributors
708
+
709
+ **TypeScript Support**:
710
+
711
+ All APIs are fully typed. See [`src/types.ts`](./src/types.ts) for advanced customization and type inference.
712
+
713
+ **Examples**:
714
+
715
+ - **Classic CLI:** [`example/launcher/classic.ts`](./example/launcher/classic.ts)
716
+ - **Modern Minimal CLI:** [`example/launcher/modern.ts`](./example/launcher/modern.ts)
717
+ - **Full Prompt Demo:** [`example/prompts/mod.ts`](./example/prompts/mod.ts)
718
+
719
+ **Components and Utilities**:
720
+
721
+ - **components/**: All prompt UIs, CLI output, launcher logic, etc.
722
+ - **utils/**: Color, error, validation, streaming, and system helpers.
723
+ - **hooks/**: Useful hooks for prompt state and effects.
724
+
484
725
  ### Helpful Links
485
726
 
486
727
  - [CLI application with the Node.js Readline module](https://dev.to/camptocamp-geo/cli-application-with-the-nodejs-readline-module-48ic)
@@ -27,7 +27,7 @@ export async function runMain<T extends ArgsDef = ArgsDef>(
27
27
  if (!meta?.version) {
28
28
  throw new CLIError("No version specified", "E_NO_VERSION");
29
29
  }
30
- relinka("info", meta.version);
30
+ relinka("log", meta.version);
31
31
  } else {
32
32
  await runCommand(cmd, { rawArgs });
33
33
  }
@@ -12,7 +12,7 @@ export async function showUsage<T extends ArgsDef = ArgsDef>(
12
12
  ) {
13
13
  try {
14
14
  // biome-ignore lint/style/useTemplate: <explanation>
15
- relinka("info", (await renderUsage(cmd, parent)) + "\n");
15
+ relinka("log", (await renderUsage(cmd, parent)) + "\n");
16
16
  } catch (error) {
17
17
  relinka("error", String(error));
18
18
  }