@reliverse/rempts 1.7.4 β†’ 1.7.5

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
@@ -1,4 +1,4 @@
1
- # rempts β€’ powerful js/ts cli builder
1
+ # πŸ“ƒ rempts β€’ powerful js/ts cli builder
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
 
@@ -6,20 +6,25 @@
6
6
 
7
7
  ## Features
8
8
 
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
14
- - 🧠 Type-safe from args to prompts
15
- - ⚑ Blazing-fast, no runtime baggage
16
- - 🧩 Router + argument parser built-in
17
- - 🎨 Customizable themes, styled output
18
- - 🚨 Crash-safe (Ctrl+C, SIGINT, errors)
19
- - πŸͺ„ Minimal API surface, max expressiveness
20
- - πŸ§ͺ Scriptable for testing, stable for production
21
- - πŸ†• Automatic commands creation (via `dler rempts init --cmd my-cool-cmd`)
22
- - 🏞️ No more hacking together `inquirer`, `citty`, `commander`, `chalk`
9
+ - πŸ«‚ rempts keeps you from fighting with your CLI tool
10
+ - ✨ rempts is your end-to-end CLI UI + command framework
11
+ - 🌿 multi-level file-based subcommands (sibling + nested)
12
+ - πŸ’ͺ built for DX precision and high-context terminal UX
13
+ - 🏎️ prompt engine that *feels* modern β€” and actually is
14
+ - πŸ“‚ file-based commands (app-router style by default)
15
+ - 🎭 looks great in plain scripts or full CLI apps
16
+ - 🧠 type-safe from args to prompts
17
+ - ⚑ blazing-fast, zero runtime baggage
18
+ - 🧩 router + argument parser built-in
19
+ - 🎨 customizable themes and styled output
20
+ - πŸ“¦ built-in output formatter and logger
21
+ - 🚨 crash-safe (Ctrl+C, SIGINT, errors)
22
+ - πŸ“ smart layout for small terminals
23
+ - πŸŽ›οΈ override styles via prompt options
24
+ - πŸͺ„ minimal API surface, maximum expressiveness
25
+ - πŸ§ͺ scriptable for testing, stable for production
26
+ - 🏞️ no more hacking together `inquirer`/`citty`/`commander`/`chalk`
27
+ - πŸ†• automatic command creation (`bun dler rempts init --cmd my-cmd`)
23
28
 
24
29
  ## Installation
25
30
 
@@ -30,9 +35,9 @@ bun add @reliverse/rempts
30
35
  **Coming soon**:
31
36
 
32
37
  ```bash
33
- bun i -g @reliverse/dler
34
- dler rempts init --cmd my-cmd-1
35
- dler rempts init --cmds
38
+ bun add -D @reliverse/dler
39
+ bun dler rempts init --cmd my-cmd-1
40
+ bun dler rempts init --cmds
36
41
  ```
37
42
 
38
43
  ## Usage Examples
@@ -217,14 +222,15 @@ export default defineCommand({
217
222
  - `arg-cmdName.{ts,js}`,
218
223
  - `cmdName/index.{ts,js}`,
219
224
  - `cmdName/cmdName-mod.{ts,js}`,
225
+ - **Multi-level subcommands:** `foo/bar/baz/cmd.ts` β†’ `my-cli foo bar baz`
220
226
  - And more β€” with automatic usage output.
221
227
 
222
228
  **Hint**:
223
229
 
224
230
  - Install `bun add -D @reliverse/dler`
225
- - Use `dler rempts init --cmd cmd1 cmd2` to init commands for rempts launcher's automatically
231
+ - Use `bun dler rempts init --cmd cmd1 cmd2` to init commands for rempts launcher's automatically
226
232
 
227
- ### Advanced Minimal API
233
+ ### Advanced Launcher Usage
228
234
 
229
235
  ```ts
230
236
  defineCommand({
@@ -247,21 +253,31 @@ defineCommand({
247
253
  - Default values, validations, descriptions
248
254
  - Full help rendering from metadata
249
255
 
250
- ### Theming + Customization
256
+ **By the way! Multi-level subcommands!**
251
257
 
252
- - Built-in output formatter and logger
253
- - Override styles via prompt options
254
- - Smart layout for small terminals
255
- - Looks great in plain scripts or full CLI apps
258
+ You can also nest subcommands arbitrarily deep:
256
259
 
257
- ### Playground
260
+ ```bash
261
+ app/
262
+ foo/
263
+ bar/
264
+ baz/
265
+ cmd.ts
266
+ ```
267
+
268
+ Invoke with:
258
269
 
259
270
  ```bash
260
- bun i -g @reliverse/rempts-cli
261
- rempts examples # supported options: name
271
+ my-cli foo bar baz --some-flag
262
272
  ```
263
273
 
264
- OR:
274
+ 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.
275
+
276
+ See [example/launcher/app/nested](./example/launcher/app/nested/) and [example/launcher/app/sibling](./example/launcher/app/sibling/) folders to learn more.
277
+
278
+ When playing with the example, you can run e.g. `bun dev:modern nested foo bar baz` to see the result in action.
279
+
280
+ ### Playground
265
281
 
266
282
  ```bash
267
283
  git clone https://github.com/reliverse/rempts
@@ -270,8 +286,9 @@ bun i
270
286
  bun dev # supported options: name
271
287
  ```
272
288
 
273
- - Both `rempts examples` from @reliverse/rempts and `bun dev` (which is the same thing) are themselves examples of `launcher` functionality.
274
- - This launcher will show you a `multiselectPrompt()` where you can choose which CLI prompts you want to play with.
289
+ - `bun dev:prompts`: This example will show you a `multiselectPrompt()` where you can choose which CLI prompts you want to play with.
290
+ - `bun dev:modern`: This example will show you a modern CLI launcher usage with file-based commands.
291
+ - `bun dev:classic`: This example will show you a classic CLI launcher usage with programmatic commands.
275
292
 
276
293
  ### Launcher Usage Examples
277
294
 
@@ -288,7 +305,7 @@ await runMain(defineCommand({}));
288
305
  **2 Run the following:**
289
306
 
290
307
  ```bash
291
- bun add -D @reliverse/dler # or: bun i -g @reliverse/dler
308
+ bun add -D @reliverse/dler
292
309
  bun dler rempts init --cmd my-cmd-1 # or: dler rempts init my-cmd-1 my-cmd-2 --main src/mod.ts
293
310
  # * `--main` is optional, default is `./src/mod.ts`
294
311
  # * you can specify multiple commands at once
@@ -428,61 +428,67 @@ async function loadSubCommand(spec) {
428
428
  throw new Error("Subcommand import did not return a valid command");
429
429
  }
430
430
  async function runFileBasedSubCmd(subName, argv, fileCmdOpts, parserOptions, parentFinish) {
431
- const subPathDir = path.join(fileCmdOpts.cmdsRootPath, subName);
432
- let importPath;
433
- const possibleFiles = [
434
- // Only allow cmd.{ts,js} inside the subcommand directory
435
- path.join(subPathDir, "cmd.js"),
436
- path.join(subPathDir, "cmd.ts")
437
- ];
438
- const dirExists = await fs.pathExists(subPathDir);
439
- let isDirCommand = false;
440
- if (dirExists) {
441
- const stats = await fs.stat(subPathDir);
442
- isDirCommand = stats.isDirectory();
443
- }
444
- if (isDirCommand) {
445
- for (const pattern of possibleFiles) {
446
- if (await fs.pathExists(pattern)) {
447
- importPath = pattern;
448
- break;
431
+ async function resolveCmdPath(baseDir, args) {
432
+ if (args.length === 0 || isFlag(args[0])) {
433
+ const possibleFiles2 = [
434
+ path.join(baseDir, "cmd.js"),
435
+ path.join(baseDir, "cmd.ts")
436
+ ];
437
+ for (const file of possibleFiles2) {
438
+ if (await fs.pathExists(file)) {
439
+ return { importPath: file, leftoverArgv: args };
440
+ }
449
441
  }
450
- }
451
- if (!importPath) {
452
- const attempted = [subName, ...argv].join(" ");
453
- const expectedPath = path.relative(
454
- process.cwd(),
455
- path.join(fileCmdOpts.cmdsRootPath, subName, "cmd.{ts,js}")
456
- );
457
442
  throw new Error(
458
- `Unknown command or arguments: ${attempted}
443
+ `Unknown command or arguments: ${args.join(" ")}
459
444
 
460
- Info for this CLI's developer: No valid command directory found, expected: ${expectedPath}`
445
+ Info for this CLI's developer: No valid command file found in ${baseDir}`
461
446
  );
462
447
  }
463
- } else {
448
+ const nextDir = path.join(baseDir, args[0]);
449
+ if (await fs.pathExists(nextDir) && (await fs.stat(nextDir)).isDirectory()) {
450
+ return resolveCmdPath(nextDir, args.slice(1));
451
+ }
452
+ const possibleFiles = [
453
+ path.join(baseDir, "cmd.js"),
454
+ path.join(baseDir, "cmd.ts")
455
+ ];
456
+ for (const file of possibleFiles) {
457
+ if (await fs.pathExists(file)) {
458
+ return { importPath: file, leftoverArgv: args };
459
+ }
460
+ }
461
+ throw new Error(
462
+ `Unknown command or arguments: ${args.join(" ")}
463
+
464
+ Info for this CLI's developer: No valid command file found in ${baseDir}`
465
+ );
466
+ }
467
+ const startDir = path.join(fileCmdOpts.cmdsRootPath, subName);
468
+ if (!await fs.pathExists(startDir) || !(await fs.stat(startDir)).isDirectory()) {
464
469
  const attempted = [subName, ...argv].join(" ");
465
- const expectedPath2 = path.relative(
470
+ const expectedPath = path.relative(
466
471
  process.cwd(),
467
472
  path.join(fileCmdOpts.cmdsRootPath, subName, "cmd.{ts,js}")
468
473
  );
469
474
  throw new Error(
470
475
  `Unknown command or arguments: ${attempted}
471
476
 
472
- Info for this CLI's developer: No valid command directory found, expected: ${expectedPath2}`
477
+ Info for this CLI's developer: No valid command directory found, expected: ${expectedPath}`
473
478
  );
474
479
  }
480
+ const { importPath, leftoverArgv } = await resolveCmdPath(startDir, argv);
475
481
  const imported = await import(path.resolve(importPath));
476
482
  const subCommand = imported.default;
477
483
  if (!subCommand) {
478
484
  throw new Error(
479
- `File-based subcommand "${subName}" has no default export or is invalid.`
485
+ `File-based subcommand has no default export or is invalid: ${importPath}`
480
486
  );
481
487
  }
482
488
  try {
483
489
  const subCtx = await runCommandWithArgs(
484
490
  subCommand,
485
- argv,
491
+ leftoverArgv,
486
492
  parserOptions,
487
493
  true
488
494
  );
package/package.json CHANGED
@@ -28,7 +28,7 @@
28
28
  "license": "MIT",
29
29
  "name": "@reliverse/rempts",
30
30
  "type": "module",
31
- "version": "1.7.4",
31
+ "version": "1.7.5",
32
32
  "author": "reliverse",
33
33
  "bugs": {
34
34
  "email": "blefnk@gmail.com",
@@ -44,7 +44,7 @@
44
44
  "devDependencies": {
45
45
  "@biomejs/biome": "1.9.4",
46
46
  "@eslint/js": "^9.26.0",
47
- "@reliverse/dler": "^1.2.2",
47
+ "@reliverse/dler": "^1.2.3",
48
48
  "@reliverse/relidler-cfg": "^1.1.3",
49
49
  "@stylistic/eslint-plugin": "^4.2.0",
50
50
  "@total-typescript/ts-reset": "^0.6.1",