@reliverse/rempts 1.6.2 → 1.7.1

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.
Files changed (89) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +311 -77
  3. package/bin/{core-impl → components}/anykey/anykey-mod.d.ts +1 -1
  4. package/bin/{core-impl → components}/anykey/anykey-mod.js +1 -1
  5. package/bin/{core-impl → components}/date/date.d.ts +1 -1
  6. package/bin/{core-impl → components}/editor/editor-mod.d.ts +1 -1
  7. package/bin/{core-impl → components}/editor/editor-mod.js +7 -7
  8. package/bin/{core-impl → components}/input/confirm-prompt.d.ts +1 -1
  9. package/bin/{core-impl → components}/input/confirm-prompt.js +1 -1
  10. package/bin/{core-impl → components}/input/input-prompt.d.ts +1 -1
  11. package/bin/{core-impl → components}/input/input-prompt.js +3 -7
  12. package/bin/{core-impl → components}/launcher/deprecated/_parser.ts.txt +1 -1
  13. package/bin/{core-impl → components}/launcher/deprecated/_utils.ts.txt +1 -1
  14. package/bin/{core-impl → components}/launcher/deprecated/args.ts.txt +1 -1
  15. package/bin/{core-impl → components}/launcher/deprecated/command.ts.txt +1 -1
  16. package/bin/{core-impl → components}/launcher/deprecated/launcher-mod.ts.txt +1 -1
  17. package/bin/{core-impl → components}/launcher/deprecated/usage.ts.txt +1 -1
  18. package/bin/components/launcher/launcher-mod.d.ts +177 -0
  19. package/bin/components/launcher/launcher-mod.js +626 -0
  20. package/bin/{core-impl → components}/msg-fmt/colors.d.ts +1 -1
  21. package/bin/{core-impl → components}/msg-fmt/logger.d.ts +1 -1
  22. package/bin/{core-impl → components}/msg-fmt/logger.js +1 -4
  23. package/bin/{core-impl → components}/msg-fmt/mapping.d.ts +1 -1
  24. package/bin/{core-impl → components}/msg-fmt/messages.d.ts +1 -1
  25. package/bin/{core-impl → components}/msg-fmt/variants.d.ts +1 -1
  26. package/bin/{core-impl → components}/next-steps/next-steps.d.ts +2 -2
  27. package/bin/{core-impl → components}/number/number-mod.d.ts +1 -1
  28. package/bin/{core-impl → components}/select/multiselect-prompt.d.ts +1 -1
  29. package/bin/{core-impl → components}/select/multiselect-prompt.js +1 -1
  30. package/bin/{core-impl → components}/select/nummultiselect-prompt.d.ts +1 -1
  31. package/bin/{core-impl → components}/select/numselect-prompt.d.ts +1 -1
  32. package/bin/{core-impl → components}/select/numselect-prompt.js +1 -1
  33. package/bin/{core-impl → components}/select/select-prompt.d.ts +1 -1
  34. package/bin/{core-impl → components}/select/select-prompt.js +2 -2
  35. package/bin/{core-impl → components}/select/toggle-prompt.d.ts +1 -1
  36. package/bin/{core-impl → components}/select/toggle-prompt.js +1 -1
  37. package/bin/{core-impl → components}/st-end/end.d.ts +1 -1
  38. package/bin/{core-impl → components}/st-end/start.d.ts +1 -1
  39. package/bin/{core-impl → components}/st-end/start.js +2 -2
  40. package/bin/{core-impl → components}/task/progress.d.ts +1 -1
  41. package/bin/{core-impl → components}/visual/animate/animate.d.ts +1 -1
  42. package/bin/hooks/spinner/spinner-mod.d.ts +20 -0
  43. package/bin/hooks/spinner/spinner-mod.js +26 -0
  44. package/bin/mod.d.ts +37 -0
  45. package/bin/mod.js +88 -0
  46. package/bin/{core-impl/utils → utils}/colorize.d.ts +1 -1
  47. package/bin/{core-impl/utils → utils}/prevent.d.ts +1 -1
  48. package/bin/{core-impl/utils → utils}/prevent.js +2 -2
  49. package/bin/{core-impl/utils → utils}/prompt-end.d.ts +1 -1
  50. package/bin/{core-impl/utils → utils}/prompt-end.js +2 -2
  51. package/bin/{core-impl/utils → utils}/stream-text.d.ts +1 -1
  52. package/bin/{core-impl/utils → utils}/stream-text.js +2 -2
  53. package/package.json +46 -24
  54. package/bin/core-impl/figures/figures.test.d.ts +0 -1
  55. package/bin/core-impl/figures/figures.test.js +0 -474
  56. package/bin/core-impl/launcher/launcher-mod.d.ts +0 -92
  57. package/bin/core-impl/launcher/launcher-mod.js +0 -371
  58. package/bin/main.d.ts +0 -36
  59. package/bin/main.js +0 -86
  60. /package/bin/{core-impl → components}/date/date.js +0 -0
  61. /package/bin/{core-impl → components}/figures/figures-mod.d.ts +0 -0
  62. /package/bin/{core-impl → components}/figures/figures-mod.js +0 -0
  63. /package/bin/{core-impl → components}/msg-fmt/colors.js +0 -0
  64. /package/bin/{core-impl → components}/msg-fmt/mapping.js +0 -0
  65. /package/bin/{core-impl → components}/msg-fmt/messages.js +0 -0
  66. /package/bin/{core-impl → components}/msg-fmt/terminal.d.ts +0 -0
  67. /package/bin/{core-impl → components}/msg-fmt/terminal.js +0 -0
  68. /package/bin/{core-impl → components}/msg-fmt/variants.js +0 -0
  69. /package/bin/{core-impl → components}/next-steps/next-steps.js +0 -0
  70. /package/bin/{core-impl → components}/number/number-mod.js +0 -0
  71. /package/bin/{core-impl → components}/results/results.d.ts +0 -0
  72. /package/bin/{core-impl → components}/results/results.js +0 -0
  73. /package/bin/{core-impl → components}/select/nummultiselect-prompt.js +0 -0
  74. /package/bin/{core-impl → components}/st-end/end.js +0 -0
  75. /package/bin/{core-impl → components}/task/progress.js +0 -0
  76. /package/bin/{core-impl → components}/task/spinner.d.ts +0 -0
  77. /package/bin/{core-impl → components}/task/spinner.js +0 -0
  78. /package/bin/{core-impl → components}/visual/animate/animate.js +0 -0
  79. /package/bin/{core-impl → components}/visual/ascii-art/ascii-art.d.ts +0 -0
  80. /package/bin/{core-impl → components}/visual/ascii-art/ascii-art.js +0 -0
  81. /package/bin/{core-types.d.ts → types.d.ts} +0 -0
  82. /package/bin/{core-types.js → types.js} +0 -0
  83. /package/bin/{core-impl/utils → utils}/colorize.js +0 -0
  84. /package/bin/{core-impl/utils → utils}/errors.d.ts +0 -0
  85. /package/bin/{core-impl/utils → utils}/errors.js +0 -0
  86. /package/bin/{core-impl/utils → utils}/system.d.ts +0 -0
  87. /package/bin/{core-impl/utils → utils}/system.js +0 -0
  88. /package/bin/{core-impl/utils → utils}/validate.d.ts +0 -0
  89. /package/bin/{core-impl/utils → utils}/validate.js +0 -0
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  # MIT License
2
2
 
3
- Copyright (c) 2025 Nazar Kornienko (blefnk), Reliverse
3
+ Copyright (c) Nazar Kornienko (blefnk), Reliverse
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -1,70 +1,176 @@
1
- # @reliverse/rempts
1
+ # rempts • powerful js/ts cli builder
2
2
 
3
- [💬 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)
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
- > @reliverse/rempts is a prompt library built with developer joy in mind. File-based commands. Flexible. Fast. Forget the clutter, it just works. This is a typesafe toolkit for building delightful CLI experiences.
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)
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
+ ## Usage Examples
31
+
32
+ - [Prompts](#prompts)
33
+ - [Launcher](#launcher)
25
34
 
26
35
  ## Screenshot
27
36
 
28
- ![Rempts Example CLI Screenshot](./example.png)
37
+ ![Rempts Example CLI Screenshot](./example/example.png)
29
38
 
30
- ## CLI Launcher (Router)
39
+ ## API Overview
31
40
 
32
- Finally, a full-featured CLI launcher without the ceremony. 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.
41
+ All main prompts APIs are available from the package root:
33
42
 
34
- **Key Features:**
43
+ ```ts
44
+ import {
45
+ // ...prompts
46
+ defineCommand, runMain, defineArgs,
47
+ inputPrompt, selectPrompt, multiselectPrompt, numberPrompt,
48
+ confirmPrompt, togglePrompt, spinnerTaskPrompt, progressTaskPrompt,
49
+ startPrompt, endPrompt, resultPrompt, nextStepsPrompt,
50
+ // ...hooks
51
+ useSpinner,
52
+ // ...launcher
53
+ runMain, defineCommand, defineArgs,
54
+ // ...types
55
+ // ...more
56
+ } from "@reliverse/rempts";
57
+ ```
35
58
 
36
- - **File-Based & Defined Subcommands:**
37
- Use `subCommands` in your command definition or let the launcher automatically load commands from a specified directory.
59
+ > See [`src/mod.ts`](./src/mod.ts) for the full list of exports.
38
60
 
39
- - **Automatic Command Detection:**
40
- The launcher scans your specified `cmdsRootPath` for command files matching common patterns such as:
41
- - `arg-cmdName.{ts,js}`
42
- - `cmdName/index.{ts,js}`
43
- - `cmdName/cmdName-mod.{ts,js}`
44
- - And more — with automatic usage output if a command file is not found.
61
+ ## Prompts
45
62
 
46
- - **Built-In Flag Handling:**
47
- Automatically processes global flags such as:
48
- - `--help` and `-h` to show usage details.
49
- - `--version` and `-v` to display version information.
50
- - `--debug` for verbose logging during development.
63
+ ### Built-in Prompts
51
64
 
52
- - **Unified Argument Parsing:**
53
- Seamlessly combines positional and named arguments with zero configuration, auto-parsing booleans, strings, numbers, arrays, and even supporting negated flags like `--no-flag`.
65
+ | Prompt | Description |
66
+ |---------------------------|-----------------------------------------------------------|
67
+ | `inputPrompt` | Single-line input (with mask support, e.g. for passwords) |
68
+ | `selectPrompt` | Single-choice radio menu |
69
+ | `multiselectPrompt` | Multi-choice checkbox menu |
70
+ | `numberPrompt` | Type-safe number input |
71
+ | `confirmPrompt` | Yes/No toggle |
72
+ | `togglePrompt` | Custom on/off toggles |
73
+ | `progressTaskPrompt` | Progress bar for async tasks |
74
+ | `resultPrompt` | Show results in a styled box |
75
+ | `nextStepsPrompt` | Show next steps in a styled list |
76
+ | `startPrompt`/`endPrompt` | Makes CLI start/end flows look nice |
77
+ | `spinnerTaskPrompt` | Async loader with spinner (possibly will be deprecated) |
78
+ | `datePrompt` | Date input with format validation |
79
+ | `anykeyPrompt` | Wait for any keypress |
54
80
 
55
- - **Customizable Behavior:**
56
- 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.
81
+ ### Hooks
57
82
 
58
- - **Error Management & Usage Output:**
59
- 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.
83
+ | Hook | Description |
84
+ |--------------|--------------------|
85
+ | `useSpinner` | Start/stop spinner |
86
+
87
+ ### Notices
88
+
89
+ - `setup`/`cleanup` are now `onCmdStart`/`onCmdEnd` (old names still work for now).
90
+
91
+ ### Prompts Usage Example
92
+
93
+ ```ts
94
+ import { relinka } from "@reliverse/relinka";
95
+
96
+ import {
97
+ startPrompt,
98
+ inputPrompt,
99
+ selectPrompt,
100
+ defineCommand,
101
+ runMain
102
+ } from "@reliverse/rempts";
103
+
104
+ async function main() {
105
+ await startPrompt({ title: "Project Setup" });
106
+
107
+ const name = await inputPrompt({
108
+ title: "What's your project name?",
109
+ defaultValue: "my-cool-project",
110
+ });
111
+
112
+ const framework = await selectPrompt({
113
+ title: "Pick your framework",
114
+ options: [
115
+ { value: "next", label: "Next.js" },
116
+ { value: "svelte", label: "SvelteKit" },
117
+ { value: "start", label: "TanStack Start" },
118
+ ],
119
+ defaultValue: "next",
120
+ });
121
+
122
+ console.log("Your result:", { name, framework });
123
+ };
124
+
125
+ await main();
126
+ ```
127
+
128
+ ## Launcher
129
+
130
+ ### Terminology
131
+
132
+ - **Launcher/Router**: The main entry point for your CLI. Visit [CLI Launcher (Router)](#cli-launcher-router) section to learn more.
133
+ - **Command/Subcommand**: A command is a function that defines the behavior of a CLI.
134
+ - **Argument**: An argument is a value that is passed to a command.
135
+ - **Flag**: A flag is a boolean argument that is used to enable or disable a feature.
136
+ - **Option**: An option is a named argument that is used to configure a command.
137
+
138
+ #### Launcher Usage Example
139
+
140
+ ‼️ Go to [Usage Examples](#usage-examples) section for a more detailed example.
141
+
142
+ ```ts
143
+ import { relinka } from "@reliverse/relinka";
144
+
145
+ import { defineCommand, runMain } from "~/mod.js";
146
+
147
+ const main = defineCommand({
148
+ meta: {
149
+ name: "rempts",
150
+ version: "1.0.0",
151
+ description: "Rempts Launcher Playground CLI",
152
+ },
153
+ onCmdStart() {
154
+ relinka("success", "Setup");
155
+ },
156
+ onCmdEnd() {
157
+ relinka("success", "Cleanup");
158
+ },
159
+ subCommands: {
160
+ build: () => import("./app/build/cmd.js").then((r) => r.default),
161
+ deploy: () => import("./app/deploy/cmd.js").then((r) => r.default),
162
+ debug: () => import("./app/debug/cmd.js").then((r) => r.default),
163
+ },
164
+ });
60
165
 
61
- **Usage Example:**
166
+ await runMain(main);
167
+ ```
62
168
 
63
169
  ```ts
64
170
  await runMain(myCommand, {
65
171
  fileBasedCmds: {
66
172
  enable: true,
67
- cmdsRootPath: "./cli/args",
173
+ cmdsRootPath: "my-cmds", // default is `./app`
68
174
  },
69
175
  // Optionally disable auto-exit to handle errors manually:
70
176
  autoExit: false,
@@ -73,11 +179,12 @@ await runMain(myCommand, {
73
179
 
74
180
  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.
75
181
 
76
- ### File-Based Subcommands
182
+ #### File-Based Subcommands
77
183
 
78
- Drop a `./src/cli/args/add/index.ts` and it's live.
184
+ Drop a `./src/cli/app/add/index.ts` and it's live.
79
185
 
80
186
  ```ts
187
+ import { defineArgs, defineCommand } from "@reliverse/rempts";
81
188
  export default defineCommand({
82
189
  meta: {
83
190
  name: "add",
@@ -85,11 +192,11 @@ export default defineCommand({
85
192
  description: "Add stuff to your project",
86
193
  },
87
194
  args: {
88
- name: {
195
+ name: defineArgs({ // 💡 PRO TIP: use defineArgs() to get fully correct intellisense
89
196
  type: "string",
90
197
  required: true,
91
198
  description: "Name of what to add",
92
- },
199
+ }),
93
200
  },
94
201
  async run({ args }) {
95
202
  relinka("info", "Adding:", args.name);
@@ -106,22 +213,10 @@ export default defineCommand({
106
213
 
107
214
  **Hint**:
108
215
 
109
- - Install `bun i -g @reliverse/rempts-cli`
110
- - Use `rempts init --cmd my-cmd-1 my-cmd-2` to init commands automatically
111
-
112
- ## 📦 Built-In Prompts
216
+ - Install `bun add -D @reliverse/dler`
217
+ - Use `dler init --cmd cmd1 cmd2` to init commands for rempts launcher's automatically
113
218
 
114
- - 🧠 `inputPrompt` – Single-line, password, masked
115
- - ✅ `selectPrompt` – Radio menu
116
- - 🧰 `multiselectPrompt` – Checkbox menu
117
- - 🔢 `numberPrompt` – Type-safe number input
118
- - 🔄 `confirmPrompt` – Yes/No toggle
119
- - 🚥 `togglePrompt` – Custom on/off toggles
120
- - ⏳ `spinnerPrompt` – Async loaders with status
121
- - 📜 `logPrompt` – Styled logs / steps
122
- - 🧼 `clearPrompt` – Clears console with style
123
-
124
- ## 🧱 Minimal, Functional API
219
+ ### Advanced Minimal API
125
220
 
126
221
  ```ts
127
222
  defineCommand({
@@ -144,14 +239,14 @@ defineCommand({
144
239
  - Default values, validations, descriptions
145
240
  - Full help rendering from metadata
146
241
 
147
- ## Theming + Customization
242
+ ### Theming + Customization
148
243
 
149
244
  - Built-in output formatter and logger
150
245
  - Override styles via prompt options
151
246
  - Smart layout for small terminals
152
247
  - Looks great in plain scripts or full CLI apps
153
248
 
154
- ## Playground
249
+ ### Playground
155
250
 
156
251
  ```bash
157
252
  bun i -g @reliverse/rempts-cli
@@ -170,7 +265,42 @@ bun dev # supported options: name
170
265
  - Both `rempts examples` from @reliverse/rempts and `bun dev` (which is the same thing) are themselves examples of `launcher` functionality.
171
266
  - This launcher will show you a `multiselectPrompt()` where you can choose which CLI prompts you want to play with.
172
267
 
173
- ### Minimal Usage Example
268
+ ### Launcher Usage Examples
269
+
270
+ #### Minimal Usage Example
271
+
272
+ **1 Create a `src/mod.ts` file:**
273
+
274
+ ```ts
275
+ import { runMain, defineCommand } from "@reliverse/rempts";
276
+
277
+ await runMain(defineCommand({}));
278
+ ```
279
+
280
+ **2 Run the following:**
281
+
282
+ ```bash
283
+ bun add -D @reliverse/dler # or: bun i -g @reliverse/dler
284
+ bun dler init --cmd my-cmd-1 # or: dler init my-cmd-1 my-cmd-2 --main src/mod.ts
285
+ # * `--main` is optional, default is `./src/mod.ts`
286
+ # * you can specify multiple commands at once
287
+ ```
288
+
289
+ **3 Visit `src/app/my-cmd-1/mod.ts` and edit it:**
290
+
291
+ ```ts
292
+ export default defineCommand({
293
+ run() { console.log("Hello, world!"); },
294
+ });
295
+ ```
296
+
297
+ **4. Test it:**
298
+
299
+ ```bash
300
+ bun src/mod.ts
301
+ ```
302
+
303
+ #### Medium Usage Example
174
304
 
175
305
  ```ts
176
306
  import { defineCommand, runMain } from "@reliverse/rempts";
@@ -187,7 +317,7 @@ const main = defineCommand({
187
317
  await runMain(main);
188
318
  ```
189
319
 
190
- ### Medium Usage Example
320
+ #### Classic Usage Example
191
321
 
192
322
  ```ts
193
323
  import { relinka } from "@reliverse/relinka";
@@ -209,18 +339,18 @@ const main = defineCommand({
209
339
  args: {
210
340
  name: {
211
341
  type: "string",
212
- required: false,
342
+ required: true,
213
343
  description: "The name of the project",
214
344
  },
215
345
  },
216
346
  async run({ args }) {
217
347
  await startPrompt({
218
- title: "🚀 Project Setup",
348
+ title: "Project Setup",
219
349
  });
220
350
 
221
351
  const name = await inputPrompt({
222
352
  title: "What's your project name?",
223
- placeholder: args.name ?? "my-cool-cli",
353
+ placeholder: args.name,
224
354
  });
225
355
 
226
356
  const framework = await selectPrompt({
@@ -239,7 +369,7 @@ const main = defineCommand({
239
369
  await runMain(main);
240
370
  ```
241
371
 
242
- ### Advanced Usage Example
372
+ #### Advanced Usage Example
243
373
 
244
374
  ```ts
245
375
  import { relinka } from "@reliverse/relinka";
@@ -258,7 +388,7 @@ import {
258
388
  * This command demonstrates the full range of launcher features along with all supported argument types:
259
389
  *
260
390
  * - Global Usage Handling: Automatically processes `--help` and `--version`.
261
- * - File-Based Subcommands: Scans "src/cli/args" for subcommands (e.g., `init`).
391
+ * - File-Based Subcommands: Scans "app" for subcommands (e.g., `init`).
262
392
  * - Comprehensive Argument Parsing: Supports positional, boolean, string, number, and array arguments.
263
393
  * - Interactive Prompts: Uses built-in prompt functions for an engaging CLI experience.
264
394
  */
@@ -273,12 +403,10 @@ const mainCommand = defineCommand({
273
403
  // Positional arguments
274
404
  inputFile: {
275
405
  type: "positional",
276
- required: false,
277
406
  description: "Path to the input file (only for the main command).",
278
407
  },
279
408
  config: {
280
409
  type: "positional",
281
- required: false,
282
410
  description: "Path to the configuration file.",
283
411
  },
284
412
  // Boolean arguments
@@ -295,20 +423,17 @@ const mainCommand = defineCommand({
295
423
  // String argument
296
424
  name: {
297
425
  type: "string",
298
- required: false,
299
426
  description: "The name of the project.",
300
427
  },
301
428
  // Number argument
302
429
  timeout: {
303
430
  type: "number",
304
- required: false,
305
431
  default: 30,
306
432
  description: "Timeout in seconds for the CLI operation.",
307
433
  },
308
434
  // Array argument
309
435
  tags: {
310
436
  type: "array",
311
- required: false,
312
437
  default: ["cli", "rempts"],
313
438
  description: "List of tags associated with the project.",
314
439
  },
@@ -322,7 +447,7 @@ const mainCommand = defineCommand({
322
447
 
323
448
  // Begin interactive session with a prompt.
324
449
  await startPrompt({
325
- title: "🚀 Project Setup",
450
+ title: "Project Setup",
326
451
  });
327
452
 
328
453
  // Ask for the project name, falling back to provided argument or a default.
@@ -358,7 +483,7 @@ const mainCommand = defineCommand({
358
483
  /**
359
484
  * The `runMain()` function sets up the launcher with several advanced features:
360
485
  *
361
- * - File-Based Subcommands: Enables scanning for subcommands within the "src/cli/args" directory.
486
+ * - File-Based Subcommands: Enables scanning for subcommands within the "app" directory.
362
487
  * - Alias Mapping: Shorthand flags (e.g., `-v`) are mapped to their full names (e.g., `--verbose`).
363
488
  * - Strict Mode & Unknown Flag Warnings: Unknown flags are either warned about or handled via a callback.
364
489
  * - Negated Boolean Support: Allows flags to be negated (e.g., `--no-verbose`).
@@ -367,7 +492,7 @@ const mainCommand = defineCommand({
367
492
  await runMain(mainCommand, {
368
493
  fileBasedCmds: {
369
494
  enable: true, // Enables file-based subcommand detection.
370
- cmdsRootPath: "src/cli/args", // Directory to scan for subcommands.
495
+ cmdsRootPath: "app", // Directory to scan for subcommands.
371
496
  },
372
497
  alias: {
373
498
  v: "verbose", // Maps shorthand flag -v to --verbose.
@@ -382,6 +507,97 @@ await runMain(mainCommand, {
382
507
  });
383
508
  ```
384
509
 
510
+ ### CLI Launcher (Router)
511
+
512
+ 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:
513
+
514
+ - **File-Based & Defined Subcommands:**
515
+ Use `subCommands` in your command definition or let the launcher automatically load commands from a specified directory.
516
+
517
+ - **Automatic Command Detection:**
518
+ The launcher scans your specified `cmdsRootPath` for command files matching common patterns such as:
519
+ - `arg-cmdName.{ts,js}`
520
+ - `cmdName/index.{ts,js}`
521
+ - `cmdName/cmdName-mod.{ts,js}`
522
+ - And more — with automatic usage output if a command file is not found.
523
+
524
+ - **Built-In Flag Handling:**
525
+ Automatically processes global flags such as:
526
+ - `--help` and `-h` to show usage details.
527
+ - `--version` and `-v` to display version information.
528
+ - `--debug` for verbose logging during development.
529
+
530
+ - **Unified Argument Parsing:**
531
+ Seamlessly combines positional and named arguments with zero configuration, auto-parsing booleans, strings, numbers, arrays, and even supporting negated flags like `--no-flag`.
532
+
533
+ - **Customizable Behavior:**
534
+ 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.
535
+
536
+ - **Error Management & Usage Output:**
537
+ 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.
538
+
539
+ - **Lifecycle Hooks:**
540
+ You can define optional lifecycle hooks in your main command:
541
+ - `onLauncherStart` and `onLauncherEnd` (global, called once per CLI process)
542
+ - `onCmdStart` and `onCmdEnd` (per-subcommand, called before/after each subcommand, but NOT for the main `run()` handler)
543
+
544
+ **Global Hooks:**
545
+ - `onLauncherStart`: Called once, before any command/subcommand/run() is executed.
546
+ - `onLauncherEnd`: Called once, after all command/subcommand/run() logic is finished (even if an error occurs).
547
+
548
+ **Per-Subcommand Hooks:**
549
+ - `onCmdStart`: Called before each subcommand (not for main `run()`).
550
+ - `onCmdEnd`: Called after each subcommand (not for main `run()`).
551
+
552
+ This means:
553
+ - If your CLI has multiple subcommands, `onCmdStart` and `onCmdEnd` will be called for each subcommand invocation, not just once for the whole CLI process.
554
+ - If your main command has a `run()` handler (and no subcommand is invoked), these hooks are **not** called; use the `run()` handler itself or the global hooks for such logic.
555
+ - This allows you to perform setup/teardown logic specific to each subcommand execution.
556
+ - If you want logic to run only once for the entire CLI process, use `onLauncherStart` and `onLauncherEnd`.
557
+
558
+ **Example:**
559
+
560
+ ```ts
561
+ const main = defineCommand({
562
+ onLauncherStart() { relinka('info', 'Global setup (once per process)'); },
563
+ onLauncherEnd() { relinka('info', 'Global cleanup (once per process)'); },
564
+ onCmdStart() { relinka('info', 'Setup for each subcommand'); },
565
+ onCmdEnd() { relinka('info', 'Cleanup for each subcommand'); },
566
+ subCommands: { ... },
567
+ run() { relinka('info', 'Main run handler (no subcommand)'); },
568
+ });
569
+ // onLauncherStart/onLauncherEnd are called once per process
570
+ // onCmdStart/onCmdEnd are called for every subcommand (not for main run())
571
+ // If you want per-run() logic, use the run() handler or global hooks
572
+ ```
573
+
574
+ > **Note:** 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.
575
+
576
+ - **Dynamic Usage Examples:**
577
+ - 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.
578
+
579
+ - **File-Based & Programmatic Subcommands:**
580
+ - Both file-based and object subcommands are fully supported. The launcher can introspect their argument definitions and metadata for help, usage, and validation.
581
+ - File-based subcommands are auto-discovered from your filesystem, while programmatic subcommands can be defined inline in your main command.
582
+
583
+ - **Context-Aware Help Output:**
584
+ - 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.
585
+
586
+ - **Error Handling:**
587
+ - 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.
588
+
589
+ - **Unified Argument Parsing:**
590
+ - All arguments (positional, named, boolean, string, number, array) are parsed and validated automatically. Negated flags (like `--no-flag`) are supported out of the box.
591
+
592
+ - **Extensible & Flexible:**
593
+ - 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.
594
+
595
+ - **Bun & Node.js Support:**
596
+ - The launcher is designed to work in both Bun and Node.js environments, so you can use it in any modern JavaScript/TypeScript project.
597
+
598
+ - **Prompt-First, Modern UX:**
599
+ - The launcher integrates tightly with the prompt engine, so you can build interactive, delightful CLIs with minimal effort.
600
+
385
601
  ## Contributing
386
602
 
387
603
  Bug report? Prompt idea? Want to build the best DX possible?
@@ -394,6 +610,24 @@ You're in the right place:
394
610
 
395
611
  > *No classes. No magic. Just clean, composable tools for CLI devs.*
396
612
 
613
+ ### Notices For Contributors
614
+
615
+ **TypeScript Support**:
616
+
617
+ All APIs are fully typed. See [`src/types.ts`](./src/types.ts) for advanced customization and type inference.
618
+
619
+ **Examples**:
620
+
621
+ - **Classic CLI:** [`example/launcher/classic.ts`](./example/launcher/classic.ts)
622
+ - **Modern Minimal CLI:** [`example/launcher/modern.ts`](./example/launcher/modern.ts)
623
+ - **Full Prompt Demo:** [`example/prompts/mod.ts`](./example/prompts/mod.ts)
624
+
625
+ **Components and Utilities**:
626
+
627
+ - **components/**: All prompt UIs, CLI output, launcher logic, etc.
628
+ - **utils/**: Color, error, validation, streaming, and system helpers.
629
+ - **hooks/**: Useful hooks for prompt state and effects.
630
+
397
631
  ### Helpful Links
398
632
 
399
633
  - [CLI application with the Node.js Readline module](https://dev.to/camptocamp-geo/cli-application-with-the-nodejs-readline-module-48ic)
@@ -1,4 +1,4 @@
1
- import type { ColorName } from "../../core-types.js";
1
+ import type { ColorName } from "../../types.js";
2
2
  type Options = {
3
3
  ctrlC?: number | false | "reject";
4
4
  preserveLog?: boolean;
@@ -2,7 +2,7 @@ import logUpdate from "log-update";
2
2
  import { cursor } from "sisteransi";
3
3
  import { fmt } from "../msg-fmt/messages.js";
4
4
  import { endPrompt } from "../st-end/end.js";
5
- import { streamText } from "../utils/stream-text.js";
5
+ import { streamText } from "../../utils/stream-text.js";
6
6
  const DEFAULT_MESSAGE = "Press any key to continue...";
7
7
  const CTRL_C_CODE = 3;
8
8
  const terminal = {
@@ -1,2 +1,2 @@
1
- import type { DatePromptOptions } from "../../core-types.js";
1
+ import type { DatePromptOptions } from "../../types.js";
2
2
  export declare function datePrompt(opts: DatePromptOptions): Promise<string>;
@@ -1,4 +1,4 @@
1
- import type { EditorExitResult } from "../../core-types.js";
1
+ import type { EditorExitResult } from "../../types.js";
2
2
  type EditorConfig = {
3
3
  syntaxHighlighting?: boolean;
4
4
  theme?: "auto" | "light" | "dark";
@@ -43,11 +43,11 @@ let state = {
43
43
  theme: {
44
44
  // Default Light Theme
45
45
  text: (str) => str,
46
- statusBarBg: (str) => re.bgGray(str),
46
+ statusBarBg: (str) => re.bgBrown(str),
47
47
  statusBarText: (str) => re.white(str),
48
- highlight: (str) => re.invert(str),
48
+ highlight: (str) => re.bgYellow(re.black(str)),
49
49
  // For search results, etc.
50
- lineNumber: (str) => re.gray(str)
50
+ lineNumber: (str) => re.blue(str)
51
51
  },
52
52
  syntaxHighlightToggle: false,
53
53
  // Toggled state for syntax highlighting
@@ -91,7 +91,7 @@ function setupTheme(configTheme) {
91
91
  } else {
92
92
  state.theme = {
93
93
  text: (str) => re.black(str),
94
- statusBarBg: (str) => re.bgGray(str),
94
+ statusBarBg: (str) => re.bgBrown(str),
95
95
  statusBarText: (str) => re.white(str),
96
96
  highlight: (str) => re.bgCyan(re.black(str)),
97
97
  lineNumber: (str) => re.gray(str)
@@ -808,10 +808,10 @@ async function initializeEditorState(options) {
808
808
  state.theme = {
809
809
  // Reset theme based on config
810
810
  text: (str) => str,
811
- statusBarBg: (str) => re.bgGray(str),
811
+ statusBarBg: (str) => re.bgBrown(str),
812
812
  statusBarText: (str) => re.white(str),
813
- highlight: (str) => re.invert(str),
814
- lineNumber: (str) => re.gray(str)
813
+ highlight: (str) => re.bgYellow(re.black(str)),
814
+ lineNumber: (str) => re.blue(str)
815
815
  };
816
816
  setupTheme(state.editorConfig.theme);
817
817
  } catch (error) {
@@ -1,4 +1,4 @@
1
- import type { ConfirmPromptOptions } from "../../core-types.js";
1
+ import type { ConfirmPromptOptions } from "../../types.js";
2
2
  /**
3
3
  * Prompts the user with a yes/no question, returning a boolean based on their input.
4
4
  */
@@ -3,7 +3,7 @@ import { stdin as input, stdout as output } from "node:process";
3
3
  import readline from "node:readline/promises";
4
4
  import { bar, msg } from "../msg-fmt/messages.js";
5
5
  import { deleteLastLine } from "../msg-fmt/terminal.js";
6
- import { completePrompt } from "../utils/prompt-end.js";
6
+ import { completePrompt } from "../../utils/prompt-end.js";
7
7
  function renderPrompt(params) {
8
8
  const {
9
9
  title,
@@ -1,4 +1,4 @@
1
- import type { InputPromptOptions } from "../../core-types.js";
1
+ import type { InputPromptOptions } from "../../types.js";
2
2
  /**
3
3
  * inputPrompt()
4
4
  *