@reliverse/rempts 1.7.5 → 1.7.7

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.
@@ -163,7 +163,10 @@ export declare function showUsage<A extends ArgDefinitions>(command: Command<A>,
163
163
  metaSettings?: {
164
164
  showDescriptionOnMain?: boolean;
165
165
  };
166
- }, displayNotFoundMessage?: boolean): Promise<void>;
166
+ _fileBasedCurrentDir?: string;
167
+ _fileBasedPathSegments?: string[];
168
+ _isSubcommand?: boolean;
169
+ }): Promise<void>;
167
170
  /**
168
171
  * Primary entry point to run a command. This function supports:
169
172
  *
@@ -105,11 +105,34 @@ async function getDefaultCliNameAndVersion() {
105
105
  return { name: "cli", version: void 0 };
106
106
  }
107
107
  }
108
- export async function showUsage(command, parserOptions = {}, displayNotFoundMessage) {
108
+ async function findDirectFileBasedSubcommands(currentDir) {
109
+ const results = [];
110
+ const items = await fs.readdir(currentDir, { withFileTypes: true });
111
+ for (const dirent of items) {
112
+ if (dirent.isDirectory()) {
113
+ for (const fname of ["cmd.ts", "cmd.js"]) {
114
+ const fpath = path.join(currentDir, dirent.name, fname);
115
+ if (await fs.pathExists(fpath)) {
116
+ try {
117
+ const imported = await import(path.resolve(fpath));
118
+ if (imported.default && !imported.default.meta?.hidden) {
119
+ results.push({ name: dirent.name, def: imported.default });
120
+ }
121
+ } catch (err) {
122
+ debugLog(`Skipping file-based subcommand in ${fpath}:`, err);
123
+ }
124
+ break;
125
+ }
126
+ }
127
+ }
128
+ }
129
+ return results;
130
+ }
131
+ export async function showUsage(command, parserOptions = {}) {
109
132
  const { name: fallbackName, version: fallbackVersion } = await getDefaultCliNameAndVersion();
110
133
  const cliName = command.meta?.name || fallbackName;
111
134
  const cliVersion = command.meta?.version || fallbackVersion;
112
- relinka("log", `${cliName}${cliVersion ? ` v${cliVersion}` : ""}`);
135
+ relinka("info", `${cliName}${cliVersion ? ` v${cliVersion}` : ""}`);
113
136
  if (parserOptions.metaSettings?.showDescriptionOnMain) {
114
137
  let description = command.meta?.description;
115
138
  if (!description) {
@@ -123,64 +146,40 @@ export async function showUsage(command, parserOptions = {}, displayNotFoundMess
123
146
  relinka("log", description);
124
147
  }
125
148
  }
149
+ const { name: pkgName } = await getDefaultCliNameAndVersion();
126
150
  const fileCmds = parserOptions.fileBasedCmds;
127
151
  if (fileCmds?.enable) {
128
- const usageLine = `Usage: ${cliName} <command> [command's options]`;
129
- relinka("log", usageLine);
130
- try {
131
- const commandsDir = path.resolve(fileCmds.cmdsRootPath);
132
- const items = await fs.readdir(commandsDir, {
133
- withFileTypes: true
134
- });
135
- const subCommandNames = [];
136
- const subCommandDefs = [];
137
- for (const dirent of items) {
138
- if (dirent.isDirectory()) {
139
- const name = dirent.name;
140
- const cmdTs = path.join(commandsDir, name, "cmd.ts");
141
- const cmdJs = path.join(commandsDir, name, "cmd.js");
142
- let imported;
143
- try {
144
- if (await fs.pathExists(cmdTs)) {
145
- imported = await import(path.resolve(cmdTs));
146
- } else if (await fs.pathExists(cmdJs)) {
147
- imported = await import(path.resolve(cmdJs));
148
- } else {
149
- continue;
150
- }
151
- if (imported.default && !imported.default.meta?.hidden) {
152
- subCommandNames.push(name);
153
- subCommandDefs.push({ name, def: imported.default });
154
- }
155
- } catch (err) {
156
- debugLog(`Skipping file-based subcommand in ${name}:`, err);
157
- }
158
- }
159
- }
160
- if (subCommandDefs.length > 0) {
161
- const randomIdx = Math.floor(Math.random() * subCommandDefs.length);
162
- const { name: exampleCmd, def: exampleDef } = subCommandDefs[randomIdx];
163
- const exampleArgs = buildExampleArgs(exampleDef.args || {});
152
+ const commandsDir = path.resolve(fileCmds.cmdsRootPath);
153
+ const currentDir = parserOptions._fileBasedCurrentDir || commandsDir;
154
+ const pathSegments = parserOptions._fileBasedPathSegments || [];
155
+ let usageLine = [pkgName, ...pathSegments].join(" ");
156
+ const directSubs = await findDirectFileBasedSubcommands(currentDir);
157
+ if (directSubs.length > 0) {
158
+ usageLine += " <command> [command's options]";
159
+ } else {
160
+ usageLine += " [command's options]";
161
+ const pos = renderPositional(command.args);
162
+ if (pos) usageLine += ` ${pos}`;
163
+ }
164
+ relinka("log", `Usage: ${usageLine}`);
165
+ if (directSubs.length > 0) {
166
+ const randomIdx = Math.floor(Math.random() * directSubs.length);
167
+ const { name: exampleCmd, def: exampleDef } = directSubs[randomIdx];
168
+ const exampleArgs = buildExampleArgs(exampleDef.args || {});
169
+ relinka(
170
+ "log",
171
+ `Example: ${pkgName} ${[...pathSegments, exampleCmd].join(" ")}${exampleArgs ? ` ${exampleArgs}` : ""}`
172
+ );
173
+ }
174
+ if (directSubs.length > 0) {
175
+ relinka("info", "Available commands (run with `help` to see more):");
176
+ directSubs.forEach(({ name, def }) => {
177
+ const desc = def?.meta?.description ?? "";
164
178
  relinka(
165
179
  "log",
166
- `Example: ${cliName} ${exampleCmd}${exampleArgs ? ` ${exampleArgs}` : ""}`
167
- );
168
- }
169
- if (subCommandNames.length > 0) {
170
- relinka("info", "Available commands (run with `help` to see more):");
171
- subCommandDefs.forEach(({ name, def }) => {
172
- const desc = def?.meta?.description ?? "";
173
- relinka("log", `\u2022 ${name}${desc ? ` | ${desc}` : ""}`);
174
- });
175
- }
176
- } catch (err) {
177
- if (displayNotFoundMessage) {
178
- relinka(
179
- "warn",
180
- `No file-based subcommands found in ${fileCmds.cmdsRootPath}`
180
+ `\u2022 ${[...pathSegments, name].join(" ")}${desc ? ` | ${desc}` : ""}`
181
181
  );
182
- }
183
- debugLog("Error reading file-based commands:", err);
182
+ });
184
183
  }
185
184
  } else {
186
185
  const subCommandNames = [];
@@ -200,7 +199,10 @@ export async function showUsage(command, parserOptions = {}, displayNotFoundMess
200
199
  }
201
200
  }
202
201
  }
203
- let usageLine = cliName;
202
+ let usageLine = pkgName;
203
+ if (parserOptions._isSubcommand) {
204
+ usageLine += ` ${cliName}`;
205
+ }
204
206
  if (subCommandNames.length > 0) {
205
207
  usageLine += " <command> [command's options]";
206
208
  } else {
@@ -213,7 +215,7 @@ export async function showUsage(command, parserOptions = {}, displayNotFoundMess
213
215
  const exampleArgs = buildExampleArgs(exampleDef.args || {});
214
216
  relinka(
215
217
  "log",
216
- `Example: ${cliName} ${exampleCmd}${exampleArgs ? ` ${exampleArgs}` : ""}`
218
+ `Example: ${pkgName}${parserOptions._isSubcommand ? ` ${cliName}` : ""} ${exampleCmd}${exampleArgs ? ` ${exampleArgs}` : ""}`
217
219
  );
218
220
  }
219
221
  if (subCommandNames.length > 0) {
@@ -309,26 +311,26 @@ This can cause recursion or unexpected behavior.`
309
311
  );
310
312
  process.exit(1);
311
313
  }
312
- if (rawArgv[0] === "help") {
313
- await showUsage(command, parserOptions);
314
- if (autoExit) process.exit(0);
315
- return;
316
- }
317
- await relinkaConfig;
318
- if (checkHelp(rawArgv)) {
319
- await showUsage(command, parserOptions);
320
- if (autoExit) process.exit(0);
321
- return;
322
- }
323
- if (checkVersion(rawArgv)) {
324
- if (command.meta?.name) {
325
- relinka(
326
- "log",
327
- `${command.meta?.name} ${command.meta?.version ? `v${command.meta?.version}` : ""}`
314
+ if (parserOptions.fileBasedCmds?.enable && rawArgv.length > 0) {
315
+ const commandsDir = path.resolve(
316
+ parserOptions.fileBasedCmds.cmdsRootPath
317
+ );
318
+ const resolved = await resolveFileBasedCommandPath(commandsDir, rawArgv);
319
+ if (resolved) {
320
+ const { def: subCommand, leftoverArgv, path: pathSegments } = resolved;
321
+ const helpIdx = leftoverArgv.findIndex(
322
+ (arg) => arg === "help" || arg === "--help" || arg === "-h"
328
323
  );
324
+ if (helpIdx !== -1) {
325
+ await showUsage(subCommand, {
326
+ ...parserOptions,
327
+ _fileBasedCurrentDir: pathSegments.length ? path.join(commandsDir, ...pathSegments) : commandsDir,
328
+ _fileBasedPathSegments: pathSegments
329
+ });
330
+ if (autoExit) process.exit(0);
331
+ return;
332
+ }
329
333
  }
330
- if (autoExit) process.exit(0);
331
- return;
332
334
  }
333
335
  const fileBasedEnabled = parserOptions.fileBasedCmds?.enable;
334
336
  if (fileBasedEnabled && rawArgv.length > 0 && !isFlag(rawArgv[0])) {
@@ -373,6 +375,18 @@ This can cause recursion or unexpected behavior.`
373
375
  }
374
376
  }
375
377
  if (subSpec) {
378
+ const helpIdx = subCmdArgv.findIndex(
379
+ (arg) => arg === "help" || arg === "--help" || arg === "-h"
380
+ );
381
+ if (helpIdx !== -1) {
382
+ const subCommandDef = await loadSubCommand(subSpec);
383
+ await showUsage(subCommandDef, {
384
+ ...parserOptions,
385
+ _isSubcommand: true
386
+ });
387
+ if (autoExit) process.exit(0);
388
+ return;
389
+ }
376
390
  try {
377
391
  const ctx = getParsedContext(command, rawArgv, parserOptions);
378
392
  if (typeof command.onCmdInit === "function")
@@ -380,7 +394,7 @@ This can cause recursion or unexpected behavior.`
380
394
  await runSubCommand(
381
395
  subSpec,
382
396
  subCmdArgv,
383
- parserOptions,
397
+ { ...parserOptions, _isSubcommand: true },
384
398
  command.onCmdExit ? async (_subCtx) => {
385
399
  await command.onCmdExit?.(ctx);
386
400
  } : void 0
@@ -394,6 +408,22 @@ This can cause recursion or unexpected behavior.`
394
408
  }
395
409
  }
396
410
  }
411
+ await relinkaConfig;
412
+ if (rawArgv[0] === "help" || checkHelp(rawArgv)) {
413
+ await showUsage(command, parserOptions);
414
+ if (autoExit) process.exit(0);
415
+ return;
416
+ }
417
+ if (checkVersion(rawArgv)) {
418
+ if (command.meta?.name) {
419
+ relinka(
420
+ "info",
421
+ `${command.meta?.name} ${command.meta?.version ? `v${command.meta?.version}` : ""}`
422
+ );
423
+ }
424
+ if (autoExit) process.exit(0);
425
+ return;
426
+ }
397
427
  try {
398
428
  await runCommandWithArgs(command, rawArgv, parserOptions);
399
429
  } finally {
@@ -500,6 +530,14 @@ Info for this CLI's developer: No valid command directory found, expected: ${exp
500
530
  async function runSubCommand(spec, argv, parserOptions, parentFinish) {
501
531
  const subCommand = await loadSubCommand(spec);
502
532
  try {
533
+ const helpIdx = argv.findIndex(
534
+ (arg) => arg === "help" || arg === "--help" || arg === "-h"
535
+ );
536
+ if (helpIdx !== -1) {
537
+ await showUsage(subCommand, { ...parserOptions, _isSubcommand: true });
538
+ if (parserOptions.autoExit !== false) process.exit(0);
539
+ return;
540
+ }
503
541
  const subCtx = await runCommandWithArgs(
504
542
  subCommand,
505
543
  argv,
@@ -603,7 +641,7 @@ async function runCommandWithArgs(command, argv, parserOptions, returnCtx) {
603
641
  const noSubcommandArgInCurrentCall = !argv.some((arg) => !isFlag(arg));
604
642
  if (isDispatcher && noSubcommandArgInCurrentCall) {
605
643
  relinka("warn", "Please specify a command");
606
- await showUsage(command, parserOptions, true);
644
+ await showUsage(command, parserOptions);
607
645
  if (autoExit) process.exit(0);
608
646
  return;
609
647
  }
@@ -792,3 +830,32 @@ function getParsedContext(command, argv, parserOptions) {
792
830
  }
793
831
  return { args: finalArgs, raw: argv };
794
832
  }
833
+ async function resolveFileBasedCommandPath(cmdsRoot, argv) {
834
+ let currentDir = cmdsRoot;
835
+ const pathSegments = [];
836
+ let leftover = [...argv];
837
+ while (leftover.length > 0 && !isFlag(leftover[0])) {
838
+ const nextDir = path.join(currentDir, leftover[0]);
839
+ if (await fs.pathExists(nextDir) && (await fs.stat(nextDir)).isDirectory()) {
840
+ currentDir = nextDir;
841
+ pathSegments.push(leftover[0]);
842
+ leftover = leftover.slice(1);
843
+ } else {
844
+ break;
845
+ }
846
+ }
847
+ for (const fname of ["cmd.ts", "cmd.js"]) {
848
+ const fpath = path.join(currentDir, fname);
849
+ if (await fs.pathExists(fpath)) {
850
+ const imported = await import(path.resolve(fpath));
851
+ if (imported.default) {
852
+ return {
853
+ def: imported.default,
854
+ path: pathSegments,
855
+ leftoverArgv: leftover
856
+ };
857
+ }
858
+ }
859
+ }
860
+ return null;
861
+ }
package/bin/mod.d.ts CHANGED
@@ -5,7 +5,7 @@ export { mainSymbols, fallbackSymbols, } from "./components/figures/figures-mod.
5
5
  export { confirmPrompt } from "./components/input/confirm-prompt.js";
6
6
  export { inputPrompt } from "./components/input/input-prompt.js";
7
7
  export type { ArgDefinition, ArgDefinitions, CommandsMap, Command, InferArgTypes, FileBasedCmdsOptions, } from "./components/launcher/launcher-mod.js";
8
- export { defineCommand, defineArgs, showUsage, runMain, } from "./components/launcher/launcher-mod.js";
8
+ export { defineCommand, defineArgs, showUsage, runMain, runCmd, } from "./components/launcher/launcher-mod.js";
9
9
  export { toBaseColor, toSolidColor } from "./components/msg-fmt/colors.js";
10
10
  export { relinkaByRemptsDeprecated, relinkaAsyncByRemptsDeprecated, throwError, } from "./components/msg-fmt/logger.js";
11
11
  export { colorMap, typographyMap } from "./components/msg-fmt/mapping.js";
package/bin/mod.js CHANGED
@@ -11,7 +11,8 @@ export {
11
11
  defineCommand,
12
12
  defineArgs,
13
13
  showUsage,
14
- runMain
14
+ runMain,
15
+ runCmd
15
16
  } from "./components/launcher/launcher-mod.js";
16
17
  export { toBaseColor, toSolidColor } from "./components/msg-fmt/colors.js";
17
18
  export {
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.5",
31
+ "version": "1.7.7",
32
32
  "author": "reliverse",
33
33
  "bugs": {
34
34
  "email": "blefnk@gmail.com",