@reliverse/rempts 1.6.2 → 1.7.0

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 (88) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +109 -22
  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/{core-impl → components}/launcher/launcher-mod.d.ts +54 -13
  19. package/bin/components/launcher/launcher-mod.js +610 -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.js +0 -371
  57. package/bin/main.d.ts +0 -36
  58. package/bin/main.js +0 -86
  59. /package/bin/{core-impl → components}/date/date.js +0 -0
  60. /package/bin/{core-impl → components}/figures/figures-mod.d.ts +0 -0
  61. /package/bin/{core-impl → components}/figures/figures-mod.js +0 -0
  62. /package/bin/{core-impl → components}/msg-fmt/colors.js +0 -0
  63. /package/bin/{core-impl → components}/msg-fmt/mapping.js +0 -0
  64. /package/bin/{core-impl → components}/msg-fmt/messages.js +0 -0
  65. /package/bin/{core-impl → components}/msg-fmt/terminal.d.ts +0 -0
  66. /package/bin/{core-impl → components}/msg-fmt/terminal.js +0 -0
  67. /package/bin/{core-impl → components}/msg-fmt/variants.js +0 -0
  68. /package/bin/{core-impl → components}/next-steps/next-steps.js +0 -0
  69. /package/bin/{core-impl → components}/number/number-mod.js +0 -0
  70. /package/bin/{core-impl → components}/results/results.d.ts +0 -0
  71. /package/bin/{core-impl → components}/results/results.js +0 -0
  72. /package/bin/{core-impl → components}/select/nummultiselect-prompt.js +0 -0
  73. /package/bin/{core-impl → components}/st-end/end.js +0 -0
  74. /package/bin/{core-impl → components}/task/progress.js +0 -0
  75. /package/bin/{core-impl → components}/task/spinner.d.ts +0 -0
  76. /package/bin/{core-impl → components}/task/spinner.js +0 -0
  77. /package/bin/{core-impl → components}/visual/animate/animate.js +0 -0
  78. /package/bin/{core-impl → components}/visual/ascii-art/ascii-art.d.ts +0 -0
  79. /package/bin/{core-impl → components}/visual/ascii-art/ascii-art.js +0 -0
  80. /package/bin/{core-types.d.ts → types.d.ts} +0 -0
  81. /package/bin/{core-types.js → types.js} +0 -0
  82. /package/bin/{core-impl/utils → utils}/colorize.js +0 -0
  83. /package/bin/{core-impl/utils → utils}/errors.d.ts +0 -0
  84. /package/bin/{core-impl/utils → utils}/errors.js +0 -0
  85. /package/bin/{core-impl/utils → utils}/system.d.ts +0 -0
  86. /package/bin/{core-impl/utils → utils}/system.js +0 -0
  87. /package/bin/{core-impl/utils → utils}/validate.d.ts +0 -0
  88. /package/bin/{core-impl/utils → utils}/validate.js +0 -0
@@ -0,0 +1,610 @@
1
+ import { reliArgParser } from "@reliverse/reliarg";
2
+ import { relinka, relinkaConfig, relinkaShutdown } from "@reliverse/relinka";
3
+ import fs from "fs-extra";
4
+ import process from "node:process";
5
+ import path from "pathe";
6
+ import { readPackageJSON } from "pkg-types";
7
+ function buildExampleArgs(args) {
8
+ const parts = [];
9
+ const positionalKeys = Object.keys(args).filter(
10
+ (k) => args[k].type === "positional"
11
+ );
12
+ positionalKeys.forEach((key) => {
13
+ const def = args[key];
14
+ if (def.required || Math.random() > 0.5) {
15
+ parts.push(String(def.default ?? `<${key}>`));
16
+ }
17
+ });
18
+ const otherKeys = Object.keys(args).filter(
19
+ (k) => args[k].type !== "positional"
20
+ );
21
+ for (const key of otherKeys) {
22
+ const def = args[key];
23
+ if (def.required || Math.random() > 0.7) {
24
+ switch (def.type) {
25
+ case "boolean":
26
+ if (def.default === true) {
27
+ if (Math.random() > 0.5) parts.push(`--no-${key}`);
28
+ } else {
29
+ parts.push(`--${key}`);
30
+ }
31
+ break;
32
+ case "string":
33
+ parts.push(`--${key}=${String(def.default ?? key)}`);
34
+ break;
35
+ case "number":
36
+ parts.push(`--${key}=${String(def.default ?? 42)}`);
37
+ break;
38
+ case "array":
39
+ if (def.options && def.options.length > 0) {
40
+ parts.push(`--${key}=${String(def.options[0])}`);
41
+ } else {
42
+ parts.push(`--${key}=${String(key)}`);
43
+ }
44
+ break;
45
+ }
46
+ }
47
+ }
48
+ return parts.join(" ");
49
+ }
50
+ const isDebugMode = process.argv.includes("--debug");
51
+ function debugLog(...args) {
52
+ if (isDebugMode) {
53
+ relinka("info", "[DEBUG]", ...args);
54
+ }
55
+ }
56
+ function isFlag(str) {
57
+ return str.startsWith("-");
58
+ }
59
+ export function defineCommand(options) {
60
+ return {
61
+ meta: options.meta,
62
+ args: options.args || {},
63
+ run: options.run,
64
+ subCommands: options.subCommands,
65
+ setup: options.setup,
66
+ cleanup: options.cleanup
67
+ };
68
+ }
69
+ let _cachedDefaultCliName;
70
+ let _cachedDefaultCliVersion;
71
+ async function getDefaultCliNameAndVersion() {
72
+ if (_cachedDefaultCliName)
73
+ return { name: _cachedDefaultCliName, version: _cachedDefaultCliVersion };
74
+ try {
75
+ const pkg = await readPackageJSON();
76
+ let name = pkg.name || "cli";
77
+ if (name.startsWith("@")) {
78
+ name = name.split("/").pop() || name;
79
+ }
80
+ _cachedDefaultCliName = name;
81
+ _cachedDefaultCliVersion = pkg.version;
82
+ return { name, version: pkg.version };
83
+ } catch (_e) {
84
+ return { name: "cli", version: void 0 };
85
+ }
86
+ }
87
+ export async function showUsage(command, parserOptions = {}, displayNotFoundMessage) {
88
+ const { name: fallbackName, version: fallbackVersion } = await getDefaultCliNameAndVersion();
89
+ const cliName = command.meta?.name || fallbackName;
90
+ const cliVersion = command.meta?.version || fallbackVersion;
91
+ relinka("info", `${cliName}${cliVersion ? ` v${cliVersion}` : ""}`);
92
+ if (parserOptions.metaSettings?.showDescriptionOnMain) {
93
+ let description = command.meta?.description;
94
+ if (!description) {
95
+ try {
96
+ const pkg = await readPackageJSON();
97
+ if (pkg.description) description = pkg.description;
98
+ } catch (_e) {
99
+ }
100
+ }
101
+ if (description) {
102
+ relinka("log", description);
103
+ }
104
+ }
105
+ const fileCmds = parserOptions.fileBasedCmds;
106
+ if (fileCmds?.enable) {
107
+ const usageLine = `Usage: ${cliName} <command> [command's options]`;
108
+ relinka("log", usageLine);
109
+ try {
110
+ const commandsDir = path.resolve(fileCmds.cmdsRootPath);
111
+ const items = await fs.readdir(commandsDir, {
112
+ withFileTypes: true
113
+ });
114
+ const subCommandNames = [];
115
+ const subCommandDefs = [];
116
+ for (const dirent of items) {
117
+ if (dirent.isDirectory()) {
118
+ const name = dirent.name;
119
+ const cmdTs = path.join(commandsDir, name, "cmd.ts");
120
+ const cmdJs = path.join(commandsDir, name, "cmd.js");
121
+ let imported;
122
+ try {
123
+ if (await fs.pathExists(cmdTs)) {
124
+ imported = await import(path.resolve(cmdTs));
125
+ } else if (await fs.pathExists(cmdJs)) {
126
+ imported = await import(path.resolve(cmdJs));
127
+ } else {
128
+ continue;
129
+ }
130
+ if (imported.default && !imported.default.meta?.hidden) {
131
+ subCommandNames.push(name);
132
+ subCommandDefs.push({ name, def: imported.default });
133
+ }
134
+ } catch (err) {
135
+ debugLog(`Skipping file-based subcommand in ${name}:`, err);
136
+ }
137
+ }
138
+ }
139
+ if (subCommandDefs.length > 0) {
140
+ const randomIdx = Math.floor(Math.random() * subCommandDefs.length);
141
+ const { name: exampleCmd, def: exampleDef } = subCommandDefs[randomIdx];
142
+ const exampleArgs = buildExampleArgs(exampleDef.args || {});
143
+ relinka(
144
+ "log",
145
+ `Example: ${cliName} ${exampleCmd}${exampleArgs ? ` ${exampleArgs}` : ""}`
146
+ );
147
+ }
148
+ if (subCommandNames.length > 0) {
149
+ relinka("info", "Available commands (run with `help` to see more):");
150
+ subCommandDefs.forEach(({ name, def }) => {
151
+ const desc = def?.meta?.description ?? "";
152
+ relinka("log", `\u2022 ${name}${desc ? ` | ${desc}` : ""}`);
153
+ });
154
+ }
155
+ } catch (err) {
156
+ if (displayNotFoundMessage) {
157
+ relinka(
158
+ "warn",
159
+ `No file-based subcommands found in ${fileCmds.cmdsRootPath}`
160
+ );
161
+ }
162
+ debugLog("Error reading file-based commands:", err);
163
+ }
164
+ } else {
165
+ const subCommandNames = [];
166
+ const subCommandDefs = [];
167
+ if (command.subCommands) {
168
+ for (const [name, spec] of Object.entries(command.subCommands)) {
169
+ try {
170
+ const cmd = await loadSubCommand(spec);
171
+ if (!cmd?.meta?.hidden) {
172
+ const aliasDisplay = cmd.meta.aliases ? ` (aliases: ${cmd.meta.aliases.join(", ")})` : "";
173
+ subCommandNames.push(`${name}${aliasDisplay}`);
174
+ subCommandDefs.push({ name, def: cmd });
175
+ }
176
+ } catch (err) {
177
+ debugLog(`Error loading subcommand ${name}:`, err);
178
+ }
179
+ }
180
+ }
181
+ let usageLine = cliName;
182
+ if (subCommandNames.length > 0) {
183
+ usageLine += " <command> [command's options]";
184
+ } else {
185
+ usageLine += ` [command's options] ${renderPositional(command.args)}`;
186
+ }
187
+ relinka("log", `Usage: ${usageLine}`);
188
+ if (subCommandDefs.length > 0) {
189
+ const randomIdx = Math.floor(Math.random() * subCommandDefs.length);
190
+ const { name: exampleCmd, def: exampleDef } = subCommandDefs[randomIdx];
191
+ const exampleArgs = buildExampleArgs(exampleDef.args || {});
192
+ relinka(
193
+ "log",
194
+ `Example: ${cliName} ${exampleCmd}${exampleArgs ? ` ${exampleArgs}` : ""}`
195
+ );
196
+ }
197
+ if (subCommandNames.length > 0) {
198
+ relinka("info", "Available commands (run with `help` to see more):");
199
+ subCommandDefs.forEach(({ name, def }) => {
200
+ const desc = def?.meta?.description ?? "";
201
+ relinka("log", `\u2022 ${name}${desc ? ` | ${desc}` : ""}`);
202
+ });
203
+ }
204
+ }
205
+ relinka("info", "Available options:");
206
+ relinka("log", "\u2022 -h, --help | Show help");
207
+ relinka("log", "\u2022 -v, --version | Show version");
208
+ relinka("log", "\u2022 --debug | Enable debug mode");
209
+ for (const [key, def] of Object.entries(command.args || {})) {
210
+ if (def.type === "positional") {
211
+ relinka(
212
+ "log",
213
+ `\u2022 <${key}> | ${def.description ?? ""}${def.required ? " | required" : ""}`
214
+ );
215
+ } else {
216
+ const parts = [
217
+ `\u2022 --${key}${"alias" in def && def.alias ? `, -${def.alias}` : ""}`,
218
+ def.description ?? "",
219
+ `type=${def.type}`
220
+ ];
221
+ if (def.default !== void 0)
222
+ parts.push(`default=${JSON.stringify(def.default)}`);
223
+ if (def.required) parts.push("required");
224
+ if (def.type === "array" && def.options)
225
+ parts.push(`options: ${def.options.join(", ")}`);
226
+ relinka("log", parts.filter(Boolean).join(" | "));
227
+ }
228
+ }
229
+ }
230
+ export async function runMain(command, parserOptions = {}) {
231
+ if (!parserOptions.fileBasedCmds && !command.subCommands) {
232
+ let callerDir = process.cwd();
233
+ let callerFile;
234
+ try {
235
+ const err = new Error();
236
+ const stack = err.stack?.split("\n");
237
+ if (stack) {
238
+ for (const line of stack) {
239
+ const match = /\((.*):(\d+):(\d+)\)/.exec(line) || /at (.*):(\d+):(\d+)/.exec(line);
240
+ if (match?.[1] && !match[1].includes("launcher-mod")) {
241
+ callerFile = match[1];
242
+ break;
243
+ }
244
+ }
245
+ }
246
+ if (callerFile) {
247
+ callerDir = path.dirname(callerFile);
248
+ const rel = path.relative(process.cwd(), callerFile);
249
+ if (/app[/][^/]+[/]cmd\.(ts|js)$/.test(rel)) {
250
+ relinka(
251
+ "error",
252
+ `runMain() should not be called from a file-based subcommand: ${rel}
253
+ This can cause recursion or unexpected behavior.
254
+ Move your runMain() call to your main CLI entry file.`
255
+ );
256
+ process.exit(1);
257
+ }
258
+ const mainEntry = process.argv[1] ? path.resolve(process.argv[1]) : void 0;
259
+ if (mainEntry && path.resolve(callerFile) !== mainEntry) {
260
+ relinka(
261
+ "error",
262
+ `runMain() should only be called from your main CLI entry file.
263
+ Detected: ${callerFile}
264
+ Main entry: ${mainEntry}
265
+ This can cause recursion or unexpected behavior.`
266
+ );
267
+ process.exit(1);
268
+ }
269
+ }
270
+ } catch (_e) {
271
+ }
272
+ const defaultCmdsRoot = path.resolve(callerDir, "app");
273
+ parserOptions.fileBasedCmds = {
274
+ enable: true,
275
+ cmdsRootPath: defaultCmdsRoot
276
+ };
277
+ }
278
+ const rawArgv = process.argv.slice(2);
279
+ const autoExit = parserOptions.autoExit !== false;
280
+ if (!(parserOptions.fileBasedCmds?.enable || command.subCommands && Object.keys(command.subCommands).length > 0 || command.run)) {
281
+ relinka(
282
+ "error",
283
+ "Invalid CLI configuration: No file-based commands, subCommands, or run() handler are defined. This CLI will not do anything.\n\u2502 To fix: add file-based commands (./app), or provide at least one subCommand or a run() handler."
284
+ );
285
+ process.exit(1);
286
+ }
287
+ if (rawArgv[0] === "help") {
288
+ await showUsage(command, parserOptions);
289
+ if (autoExit) process.exit(0);
290
+ return;
291
+ }
292
+ await relinkaConfig;
293
+ if (checkHelp(rawArgv)) {
294
+ await showUsage(command, parserOptions);
295
+ if (autoExit) process.exit(0);
296
+ return;
297
+ }
298
+ if (checkVersion(rawArgv)) {
299
+ if (command.meta?.name) {
300
+ relinka(
301
+ "info",
302
+ `${command.meta?.name} ${command.meta?.version ? `v${command.meta?.version}` : ""}`
303
+ );
304
+ }
305
+ if (autoExit) process.exit(0);
306
+ return;
307
+ }
308
+ const fileBasedEnabled = parserOptions.fileBasedCmds?.enable;
309
+ if (fileBasedEnabled && rawArgv.length > 0 && !isFlag(rawArgv[0])) {
310
+ const [subName, ...subCmdArgv] = rawArgv;
311
+ try {
312
+ if (typeof command.setup === "function") await command.setup();
313
+ await runFileBasedSubCmd(
314
+ subName,
315
+ subCmdArgv,
316
+ parserOptions.fileBasedCmds,
317
+ parserOptions,
318
+ command.cleanup
319
+ );
320
+ if (autoExit) process.exit(0);
321
+ return;
322
+ } catch (err) {
323
+ relinka("error", "Error loading file-based subcommand:", err.message);
324
+ if (autoExit) process.exit(1);
325
+ throw err;
326
+ }
327
+ }
328
+ if (!fileBasedEnabled && command.subCommands && rawArgv.length > 0 && !isFlag(rawArgv[0])) {
329
+ const [maybeSub, ...subCmdArgv] = rawArgv;
330
+ let subSpec;
331
+ for (const [key, spec] of Object.entries(command.subCommands)) {
332
+ if (key === maybeSub) {
333
+ subSpec = spec;
334
+ break;
335
+ }
336
+ try {
337
+ const cmd = await loadSubCommand(spec);
338
+ if (cmd.meta.aliases?.includes(maybeSub)) {
339
+ subSpec = spec;
340
+ break;
341
+ }
342
+ } catch (err) {
343
+ debugLog(`Error checking alias for subcommand ${key}:`, err);
344
+ }
345
+ }
346
+ if (subSpec) {
347
+ try {
348
+ if (typeof command.setup === "function") await command.setup();
349
+ await runSubCommand(
350
+ subSpec,
351
+ subCmdArgv,
352
+ parserOptions,
353
+ command.cleanup
354
+ );
355
+ if (autoExit) process.exit(0);
356
+ return;
357
+ } catch (err) {
358
+ relinka("error", "Error running subcommand:", err.message);
359
+ if (autoExit) process.exit(1);
360
+ throw err;
361
+ }
362
+ }
363
+ }
364
+ if (typeof command.setup === "function") await command.setup();
365
+ try {
366
+ await runCommandWithArgs(command, rawArgv, parserOptions);
367
+ } finally {
368
+ if (typeof command.cleanup === "function") await command.cleanup();
369
+ }
370
+ await relinkaShutdown();
371
+ }
372
+ function checkHelp(argv) {
373
+ return argv.includes("--help") || argv.includes("-h");
374
+ }
375
+ function checkVersion(argv) {
376
+ return argv.includes("--version") || argv.includes("-v");
377
+ }
378
+ async function loadSubCommand(spec) {
379
+ if (typeof spec === "string") {
380
+ const mod = await import(spec);
381
+ if (!mod.default) {
382
+ throw new Error(`Subcommand module "${spec}" has no default export`);
383
+ }
384
+ return mod.default;
385
+ }
386
+ const imported = await spec();
387
+ if ("default" in imported && imported.default) {
388
+ return imported.default;
389
+ }
390
+ if (imported) {
391
+ return imported;
392
+ }
393
+ throw new Error("Subcommand import did not return a valid command");
394
+ }
395
+ async function runFileBasedSubCmd(subName, argv, fileCmdOpts, parserOptions, parentCleanup) {
396
+ const subPathDir = path.join(fileCmdOpts.cmdsRootPath, subName);
397
+ let importPath;
398
+ const possibleFiles = [
399
+ // Only allow cmd.{ts,js} inside the subcommand directory
400
+ path.join(subPathDir, "cmd.js"),
401
+ path.join(subPathDir, "cmd.ts")
402
+ ];
403
+ const dirExists = await fs.pathExists(subPathDir);
404
+ let isDirCommand = false;
405
+ if (dirExists) {
406
+ const stats = await fs.stat(subPathDir);
407
+ isDirCommand = stats.isDirectory();
408
+ }
409
+ if (isDirCommand) {
410
+ for (const pattern of possibleFiles) {
411
+ if (await fs.pathExists(pattern)) {
412
+ importPath = pattern;
413
+ break;
414
+ }
415
+ }
416
+ if (!importPath) {
417
+ const attempted = [subName, ...argv].join(" ");
418
+ const expectedPath = path.relative(
419
+ process.cwd(),
420
+ path.join(fileCmdOpts.cmdsRootPath, subName, "cmd.{ts,js}")
421
+ );
422
+ throw new Error(
423
+ `Unknown command or arguments: ${attempted}
424
+
425
+ Info for this CLI's developer: No valid command directory found, expected: ${expectedPath}`
426
+ );
427
+ }
428
+ } else {
429
+ const attempted = [subName, ...argv].join(" ");
430
+ const expectedPath2 = path.relative(
431
+ process.cwd(),
432
+ path.join(fileCmdOpts.cmdsRootPath, subName, "cmd.{ts,js}")
433
+ );
434
+ throw new Error(
435
+ `Unknown command or arguments: ${attempted}
436
+
437
+ Info for this CLI's developer: No valid command directory found, expected: ${expectedPath2}`
438
+ );
439
+ }
440
+ const imported = await import(path.resolve(importPath));
441
+ const subCommand = imported.default;
442
+ if (!subCommand) {
443
+ throw new Error(
444
+ `File-based subcommand "${subName}" has no default export or is invalid.`
445
+ );
446
+ }
447
+ try {
448
+ await runCommandWithArgs(subCommand, argv, parserOptions);
449
+ } finally {
450
+ if (typeof parentCleanup === "function") await parentCleanup();
451
+ }
452
+ }
453
+ async function runSubCommand(spec, argv, parserOptions, parentCleanup) {
454
+ const subCommand = await loadSubCommand(spec);
455
+ try {
456
+ await runCommandWithArgs(subCommand, argv, parserOptions);
457
+ } finally {
458
+ if (typeof parentCleanup === "function") await parentCleanup();
459
+ }
460
+ }
461
+ async function runCommandWithArgs(command, argv, parserOptions) {
462
+ const autoExit = parserOptions.autoExit !== false;
463
+ const booleanKeys = Object.keys(command.args || {}).filter(
464
+ (k) => command.args[k].type === "boolean"
465
+ );
466
+ const defaultMap = {};
467
+ for (const [argKey, def] of Object.entries(command.args || {})) {
468
+ if (def.default !== void 0) {
469
+ if (def.type === "array" && typeof def.default === "string") {
470
+ defaultMap[argKey] = [def.default];
471
+ } else {
472
+ defaultMap[argKey] = def.default;
473
+ }
474
+ }
475
+ }
476
+ const mergedParserOptions = {
477
+ ...parserOptions,
478
+ boolean: [...parserOptions.boolean || [], ...booleanKeys],
479
+ defaults: { ...defaultMap, ...parserOptions.defaults || {} }
480
+ };
481
+ const parsed = reliArgParser(argv, mergedParserOptions);
482
+ debugLog("Parsed arguments:", parsed);
483
+ const finalArgs = {};
484
+ const positionalKeys = Object.keys(command.args || {}).filter(
485
+ (k) => command.args[k].type === "positional"
486
+ );
487
+ const leftoverPositionals = [...parsed._ || []];
488
+ for (let i = 0; i < positionalKeys.length; i++) {
489
+ const key = positionalKeys[i];
490
+ const def = command.args?.[key];
491
+ const val = leftoverPositionals[i];
492
+ if (val == null && def.required) {
493
+ relinka("error", `Missing required positional argument: <${key}>`);
494
+ await showUsage(command, parserOptions);
495
+ if (autoExit) process.exit(1);
496
+ else throw new Error(`Missing required positional argument: <${key}>`);
497
+ }
498
+ finalArgs[key] = val != null ? castArgValue(def, val, key) : def.default;
499
+ }
500
+ const otherKeys = Object.keys(command.args || {}).filter(
501
+ (k) => command.args[k].type !== "positional"
502
+ );
503
+ for (const key of otherKeys) {
504
+ const def = command.args?.[key];
505
+ let rawVal = parsed[key];
506
+ if (def.type === "array" && rawVal !== void 0 && !Array.isArray(rawVal)) {
507
+ rawVal = [rawVal];
508
+ }
509
+ const valueOrDefault = rawVal ?? defaultMap[key];
510
+ if (valueOrDefault == null && def.required) {
511
+ relinka("error", `Missing required argument: --${key}`);
512
+ await showUsage(command, parserOptions);
513
+ if (autoExit) process.exit(1);
514
+ else throw new Error(`Missing required argument: --${key}`);
515
+ }
516
+ try {
517
+ finalArgs[key] = castArgValue(def, rawVal, key);
518
+ if (def.type === "array" && def.options && finalArgs[key]) {
519
+ const values = finalArgs[key];
520
+ const invalidOptions = values.filter(
521
+ (v) => def.options && !def.options.includes(v)
522
+ );
523
+ if (invalidOptions.length > 0) {
524
+ throw new Error(
525
+ `Invalid choice(s) for --${key}: ${invalidOptions.join(", ")}. Allowed options: ${def.options.join(", ")}`
526
+ );
527
+ }
528
+ }
529
+ } catch (err) {
530
+ relinka("error", String(err));
531
+ if (autoExit) process.exit(1);
532
+ else throw err;
533
+ }
534
+ }
535
+ const ctx = {
536
+ args: finalArgs,
537
+ raw: argv
538
+ };
539
+ try {
540
+ if (command.run) {
541
+ await command.run(ctx);
542
+ } else {
543
+ const isDispatcher = parserOptions.fileBasedCmds?.enable || command.subCommands && Object.keys(command.subCommands).length > 0;
544
+ const noSubcommandArgInCurrentCall = !argv.some((arg) => !isFlag(arg));
545
+ if (isDispatcher && noSubcommandArgInCurrentCall) {
546
+ relinka("warn", "Please specify a command");
547
+ await showUsage(command, parserOptions, true);
548
+ if (autoExit) process.exit(0);
549
+ return;
550
+ }
551
+ const cmdName = command.meta?.name || "unknown";
552
+ const attempted = argv.length > 0 ? argv.join(" ") : "(no arguments)";
553
+ relinka("error", `Unknown command or arguments: ${attempted}`);
554
+ await showUsage(command, parserOptions);
555
+ if (autoExit) {
556
+ process.exit(1);
557
+ } else {
558
+ throw new Error(`Command "${cmdName}" is not runnable.`);
559
+ }
560
+ }
561
+ } catch (err) {
562
+ relinka("error", `Error while executing command:
563
+ ${String(err)}`);
564
+ if (autoExit) process.exit(1);
565
+ else throw err;
566
+ }
567
+ }
568
+ function castArgValue(def, rawVal, argName) {
569
+ if (rawVal == null) {
570
+ if (def.type === "array" && typeof def.default === "string") {
571
+ return [def.default];
572
+ }
573
+ return def.default ?? void 0;
574
+ }
575
+ switch (def.type) {
576
+ case "boolean":
577
+ if (typeof rawVal === "string") {
578
+ const lower = rawVal.toLowerCase();
579
+ if (lower === "true") return true;
580
+ if (lower === "false") return false;
581
+ }
582
+ return Boolean(rawVal);
583
+ case "string":
584
+ return typeof rawVal === "string" ? rawVal : String(rawVal);
585
+ case "number": {
586
+ const n = Number(rawVal);
587
+ if (Number.isNaN(n)) {
588
+ throw new Error(`Invalid number provided for --${argName}: ${rawVal}`);
589
+ }
590
+ return n;
591
+ }
592
+ case "positional":
593
+ return String(rawVal);
594
+ case "array": {
595
+ const arrVal = Array.isArray(rawVal) ? rawVal : [String(rawVal)];
596
+ return arrVal.map(String);
597
+ }
598
+ default:
599
+ return rawVal;
600
+ }
601
+ }
602
+ function renderPositional(args) {
603
+ const positionalKeys = Object.keys(args || {}).filter(
604
+ (k) => args[k].type === "positional"
605
+ );
606
+ return positionalKeys.map((k) => `<${k}>`).join(" ");
607
+ }
608
+ export function defineArgs(args) {
609
+ return args;
610
+ }
@@ -1,4 +1,4 @@
1
- import type { ColorName, OutputColor, StandardColor } from "../../core-types.js";
1
+ import type { ColorName, OutputColor, StandardColor } from "../../types.js";
2
2
  /**
3
3
  * Maps complex colors (gradients, bright, background) to standard terminal colors.
4
4
  * Used by ora spinners and other terminal utilities that only support basic colors.
@@ -1,4 +1,4 @@
1
- import type { AllKinds, StreamOptions } from "../../core-types.js";
1
+ import type { AllKinds, StreamOptions } from "../../types.js";
2
2
  /**
3
3
  * Logs messages with configurable styling and formatting.
4
4
  * Doesn't support streaming functionality.
@@ -1,7 +1,4 @@
1
- import {
2
- streamText,
3
- streamTextWithSpinner
4
- } from "../utils/stream-text.js";
1
+ import { streamText, streamTextWithSpinner } from "../../utils/stream-text.js";
5
2
  import { toSolidColor } from "./colors.js";
6
3
  import { msg } from "./messages.js";
7
4
  const verboseLogging = false;
@@ -1,3 +1,3 @@
1
- import type { ColorName, TypographyName } from "../../core-types.js";
1
+ import type { ColorName, TypographyName } from "../../types.js";
2
2
  export declare const colorMap: Record<ColorName, (text: string) => string>;
3
3
  export declare const typographyMap: Record<TypographyName, (text: string) => string>;
@@ -1,4 +1,4 @@
1
- import type { ColorName, FmtMsgOptions, Symbols } from "../../core-types.js";
1
+ import type { ColorName, FmtMsgOptions, Symbols } from "../../types.js";
2
2
  export declare const symbols: Symbols;
3
3
  /**
4
4
  * Returns a colored vertical bar symbol. Prevents gradient colors for bars.
@@ -1,4 +1,4 @@
1
- import type { ColorName, VariantName } from "../../core-types.js";
1
+ import type { ColorName, VariantName } from "../../types.js";
2
2
  export declare const variantMap: {
3
3
  doubleBox: typeof createDoubleBox;
4
4
  };
@@ -1,5 +1,5 @@
1
- import type { TypographyName, ColorName } from "../../core-types.js";
2
- import type { VariantName } from "../../core-types.js";
1
+ import type { TypographyName, ColorName } from "../../types.js";
2
+ import type { VariantName } from "../../types.js";
3
3
  type NextStepsPromptOptions = {
4
4
  title?: string;
5
5
  titleColor?: ColorName;
@@ -1,4 +1,4 @@
1
- import type { BorderColorName, ColorName, FmtMsgOptions, TypographyName } from "../../core-types.js";
1
+ import type { BorderColorName, ColorName, FmtMsgOptions, TypographyName } from "../../types.js";
2
2
  type VariantName = FmtMsgOptions["titleVariant"];
3
3
  type NumberPromptOptions = {
4
4
  title: string;
@@ -1,2 +1,2 @@
1
- import type { MultiselectPromptParams } from "../../core-types.js";
1
+ import type { MultiselectPromptParams } from "../../types.js";
2
2
  export declare function multiselectPrompt<T extends string>(params: MultiselectPromptParams<T>): Promise<T[]>;
@@ -4,7 +4,7 @@ import { stdin as input, stdout as output } from "node:process";
4
4
  import readline from "node:readline";
5
5
  import { msg, symbols } from "../msg-fmt/messages.js";
6
6
  import { deleteLastLine } from "../msg-fmt/terminal.js";
7
- import { completePrompt } from "../utils/prompt-end.js";
7
+ import { completePrompt } from "../../utils/prompt-end.js";
8
8
  function isSelectOption(option) {
9
9
  return !("separator" in option);
10
10
  }
@@ -1,4 +1,4 @@
1
- import type { PromptOptions } from "../../core-types.js";
1
+ import type { PromptOptions } from "../../types.js";
2
2
  type NumMultiSelectPromptOptions = PromptOptions & {
3
3
  defaultValue?: string[];
4
4
  };