gunshi 0.27.6 → 0.28.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.
package/lib/bone.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { A as SubCommandable, C as GunshiParams, D as Prettify, E as NormalizeToGunshiParams, O as RendererDecorator, S as ExtractExtensions, T as LazyCommand, _ as Commandable, a as Awaitable, b as ExtractArgExplicitlyProvided, c as CommandCallMode, d as CommandContextExtension, f as CommandDecorator, g as CommandRunner, h as CommandLoader, i as Args, j as ValidationErrorsDecorator, k as RenderingOptions, l as CommandContext, m as CommandExamplesFetcher, n as ArgToken, o as CliOptions, p as CommandEnvironment, r as ArgValues, s as Command, t as ArgSchema, u as CommandContextCore, v as DefaultGunshiParams, w as GunshiParamsConstraint, x as ExtractArgs, y as ExtendContext } from "./types-RXstkMKh.js";
1
+ import { A as SubCommandable, C as GunshiParams, D as Prettify, E as NormalizeToGunshiParams, O as RendererDecorator, S as ExtractExtensions, T as LazyCommand, _ as Commandable, a as Awaitable, b as ExtractArgExplicitlyProvided, c as CommandCallMode, d as CommandContextExtension, f as CommandDecorator, g as CommandRunner, h as CommandLoader, i as Args, j as ValidationErrorsDecorator, k as RenderingOptions, l as CommandContext, m as CommandExamplesFetcher, n as ArgToken, o as CliOptions, p as CommandEnvironment, r as ArgValues, s as Command, t as ArgSchema, u as CommandContextCore, v as DefaultGunshiParams, w as GunshiParamsConstraint, x as ExtractArgs, y as ExtendContext } from "./types-DSyU38G8.js";
2
2
 
3
3
  //#region src/cli/bone.d.ts
4
4
  /**
package/lib/bone.js CHANGED
@@ -1,4 +1,4 @@
1
- import { t as cliCore } from "./core-BEHAY3pE.js";
1
+ import { t as cliCore } from "./core-C9P_Gd_f.js";
2
2
 
3
3
  //#region src/cli/bone.ts
4
4
  /**
@@ -1,6 +1,6 @@
1
- import { t as cliCore } from "./core-BEHAY3pE.js";
1
+ import { t as cliCore } from "./core-C9P_Gd_f.js";
2
2
  import { t as plugin } from "./core-eN5xmB3s.js";
3
- import { a as namespacedId, s as COMMON_ARGS, t as renderer } from "./src-DulE0ns5.js";
3
+ import { a as namespacedId, s as COMMON_ARGS, t as renderer } from "./src-CfO5ab2f.js";
4
4
 
5
5
  //#region ../plugin-global/src/types.ts
6
6
  /**
@@ -1,4 +1,4 @@
1
- import { o as CliOptions, v as DefaultGunshiParams } from "./types-RXstkMKh.js";
1
+ import { o as CliOptions, v as DefaultGunshiParams } from "./types-DSyU38G8.js";
2
2
 
3
3
  //#region src/constants.d.ts
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { i as log, n as deepFreeze, r as isLazyCommand, t as create } from "./utils-A01g1U8-.js";
1
+ import { a as log, i as isLazyCommand, n as deepFreeze, t as create } from "./utils-DNS9AIR_.js";
2
2
  import { n as CLI_OPTIONS_DEFAULT, r as NOOP, t as ANONYMOUS_COMMAND_NAME } from "./constants-WviiKqbt.js";
3
3
 
4
4
  //#region src/context.ts
@@ -23,7 +23,7 @@ import { n as CLI_OPTIONS_DEFAULT, r as NOOP, t as ANONYMOUS_COMMAND_NAME } from
23
23
  * @param param - A {@link CommandContextParams | parameters} to create a command context.
24
24
  * @returns A {@link CommandContext | command context}, which is readonly.
25
25
  */
26
- async function createCommandContext({ args = {}, explicit = {}, values = {}, positionals = [], rest = [], argv = [], tokens = [], command = {}, extensions = {}, cliOptions = {}, callMode = "entry", omitted = false, validationError = void 0 }) {
26
+ async function createCommandContext({ args = {}, explicit = {}, values = {}, positionals = [], rest = [], argv = [], tokens = [], command = {}, extensions = {}, cliOptions = {}, callMode = "entry", commandPath = [], omitted = false, validationError = void 0 }) {
27
27
  /**
28
28
  * normailize the options schema and values, to avoid prototype pollution
29
29
  */
@@ -52,6 +52,7 @@ async function createCommandContext({ args = {}, explicit = {}, values = {}, pos
52
52
  description: command.description,
53
53
  omitted,
54
54
  callMode,
55
+ commandPath,
55
56
  env,
56
57
  args: _args,
57
58
  explicit,
package/lib/context.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { C as GunshiParams, T as LazyCommand, b as ExtractArgExplicitlyProvided, c as CommandCallMode, d as CommandContextExtension, i as Args, l as CommandContext, n as ArgToken, o as CliOptions, r as ArgValues, s as Command, v as DefaultGunshiParams, w as GunshiParamsConstraint, x as ExtractArgs, y as ExtendContext } from "./types-RXstkMKh.js";
1
+ import { C as GunshiParams, T as LazyCommand, b as ExtractArgExplicitlyProvided, c as CommandCallMode, d as CommandContextExtension, i as Args, l as CommandContext, n as ArgToken, o as CliOptions, r as ArgValues, s as Command, v as DefaultGunshiParams, w as GunshiParamsConstraint, x as ExtractArgs, y as ExtendContext } from "./types-DSyU38G8.js";
2
2
 
3
3
  //#region src/context.d.ts
4
4
 
@@ -52,6 +52,10 @@ interface CommandContextParams<G extends GunshiParams | {
52
52
  * Command call mode.
53
53
  */
54
54
  callMode?: CommandCallMode;
55
+ /**
56
+ * The path of nested sub-commands resolved to reach the current command.
57
+ */
58
+ commandPath?: string[];
55
59
  /**
56
60
  * A target command
57
61
  */
@@ -87,6 +91,7 @@ declare function createCommandContext<G extends GunshiParamsConstraint = Default
87
91
  extensions,
88
92
  cliOptions,
89
93
  callMode,
94
+ commandPath,
90
95
  omitted,
91
96
  validationError
92
97
  }: CommandContextParams<G, V, C, E>): Promise<{} extends ExtractExtensions<E> ? Readonly<CommandContext<G>> : Readonly<CommandContext<GunshiParams<{
package/lib/context.js CHANGED
@@ -1,3 +1,3 @@
1
- import { t as createCommandContext } from "./context-2cGqZAlo.js";
1
+ import { t as createCommandContext } from "./context-CHDIbgwd.js";
2
2
 
3
3
  export { createCommandContext };
@@ -1,6 +1,6 @@
1
- import { a as resolveLazyCommand, o as kebabnize, r as isLazyCommand, t as create } from "./utils-A01g1U8-.js";
1
+ import { i as isLazyCommand, o as resolveLazyCommand, r as getCommandSubCommands, s as kebabnize, t as create } from "./utils-DNS9AIR_.js";
2
2
  import { n as CLI_OPTIONS_DEFAULT, r as NOOP, t as ANONYMOUS_COMMAND_NAME } from "./constants-WviiKqbt.js";
3
- import { t as createCommandContext } from "./context-2cGqZAlo.js";
3
+ import { t as createCommandContext } from "./context-CHDIbgwd.js";
4
4
 
5
5
  //#region ../../node_modules/.pnpm/args-tokens@0.23.1/node_modules/args-tokens/lib/parser.js
6
6
  const HYPHEN_CHAR = "-";
@@ -664,16 +664,18 @@ async function cliCore(argv, entry, options, plugins) {
664
664
  const resolvedPlugins = await applyPlugins(pluginContext, [...plugins, ...options.plugins || []]);
665
665
  const cliOptions = normalizeCliOptions(options, decorators, pluginContext);
666
666
  const tokens = parseArgs(argv);
667
- const subCommand = getSubCommand(tokens);
668
- const { commandName: name, command, callMode } = resolveCommand(subCommand, entry, cliOptions);
667
+ const resolved = resolveCommandTree(tokens, entry, cliOptions);
668
+ const { commandName: name, command, callMode, commandPath, depth, levelSubCommands } = resolved;
669
669
  if (!command) throw new Error(`Command not found: ${name || ""}`);
670
670
  const args = resolveArguments(pluginContext, getCommandArgs(command));
671
+ const skipPositional = depth > 0 ? depth - 1 : -1;
671
672
  const { explicit, values, positionals, rest, error } = resolveArgs(args, tokens, {
672
673
  shortGrouping: true,
673
674
  toKebab: command.toKebab,
674
- skipPositional: callMode === "subCommand" && cliOptions.subCommands.size > 0 ? 0 : -1
675
+ skipPositional
675
676
  });
676
- const omitted = !subCommand;
677
+ const omitted = resolved.omitted;
678
+ if (levelSubCommands) cliOptions.subCommands = levelSubCommands;
677
679
  const resolvedCommand = isLazyCommand(command) ? await resolveLazyCommand(command, name, true) : command;
678
680
  return await executeCommand(resolvedCommand, await createCommandContext({
679
681
  args,
@@ -685,6 +687,7 @@ async function cliCore(argv, entry, options, plugins) {
685
687
  tokens,
686
688
  omitted,
687
689
  callMode,
690
+ commandPath,
688
691
  command: resolvedCommand,
689
692
  extensions: getPluginExtensions(resolvedPlugins),
690
693
  validationError: error,
@@ -721,9 +724,12 @@ function createInitialSubCommands(options, entryCmd) {
721
724
  const subCommands = new Map(options.subCommands instanceof Map ? options.subCommands : []);
722
725
  if (!(options.subCommands instanceof Map) && isObject(options.subCommands)) for (const [name, cmd] of Object.entries(options.subCommands)) subCommands.set(name, cmd);
723
726
  if (hasSubCommands) {
724
- if (isLazyCommand(entryCmd) || typeof entryCmd === "object") {
725
- entryCmd.entry = true;
726
- subCommands.set(resolveEntryName(entryCmd), entryCmd);
727
+ if (isLazyCommand(entryCmd)) {
728
+ const entryCopy = Object.assign((...args) => entryCmd(...args), entryCmd, { entry: true });
729
+ subCommands.set(resolveEntryName(entryCopy), entryCopy);
730
+ } else if (typeof entryCmd === "object") {
731
+ const entryCopy = Object.assign(create(), entryCmd, { entry: true });
732
+ subCommands.set(resolveEntryName(entryCopy), entryCopy);
727
733
  } else if (typeof entryCmd === "function") {
728
734
  const name = entryCmd.name || ANONYMOUS_COMMAND_NAME;
729
735
  subCommands.set(name, {
@@ -743,48 +749,102 @@ function normalizeCliOptions(options, decorators, pluginContext) {
743
749
  if (resolvedOptions.renderValidationErrors === void 0) resolvedOptions.renderValidationErrors = decorators.getValidationErrorsRenderer();
744
750
  return resolvedOptions;
745
751
  }
746
- function getSubCommand(tokens) {
747
- const firstToken = tokens[0];
748
- return firstToken && firstToken.kind === "positional" && firstToken.index === 0 && firstToken.value ? firstToken.value : "";
752
+ function getPositionalTokens(tokens) {
753
+ return tokens.filter((t) => t.kind === "positional").map((t) => t.value).filter((v) => !!v);
749
754
  }
750
- const CANNOT_RESOLVE_COMMAND = { callMode: "unexpected" };
751
- function resolveCommand(sub, entry, options) {
752
- const omitted = !sub;
753
- function doResolveCommand() {
755
+ function resolveCommandTree(tokens, entry, options) {
756
+ const positionals = getPositionalTokens(tokens);
757
+ function resolveAsEntry() {
754
758
  if (typeof entry === "function") if ("commandName" in entry && entry.commandName) return {
755
759
  commandName: entry.commandName,
756
760
  command: entry,
757
- callMode: "entry"
761
+ callMode: "entry",
762
+ commandPath: [],
763
+ depth: 0,
764
+ omitted: options.subCommands.size > 0 && !positionals[0],
765
+ levelSubCommands: options.subCommands.size > 0 ? options.subCommands : void 0
758
766
  };
759
767
  else return {
760
768
  command: {
761
769
  run: entry,
762
770
  entry: true
763
771
  },
764
- callMode: "entry"
772
+ callMode: "entry",
773
+ commandPath: [],
774
+ depth: 0,
775
+ omitted: options.subCommands.size > 0 && !positionals[0],
776
+ levelSubCommands: options.subCommands.size > 0 ? options.subCommands : void 0
765
777
  };
766
778
  else if (typeof entry === "object") return {
767
779
  commandName: resolveEntryName(entry),
768
780
  command: entry,
769
- callMode: "entry"
781
+ callMode: "entry",
782
+ commandPath: [],
783
+ depth: 0,
784
+ omitted: options.subCommands.size > 0 && !positionals[0],
785
+ levelSubCommands: options.subCommands.size > 0 ? options.subCommands : void 0
770
786
  };
771
- else return CANNOT_RESOLVE_COMMAND;
772
- }
773
- if (omitted || options.subCommands?.size === 0) return doResolveCommand();
774
- const cmd = options.subCommands?.get(sub);
775
- if (cmd == null) {
776
- if (options.fallbackToEntry) return doResolveCommand();
777
- return {
778
- commandName: sub,
779
- callMode: "unexpected"
787
+ else return {
788
+ callMode: "unexpected",
789
+ commandPath: [],
790
+ depth: 0,
791
+ omitted: false,
792
+ levelSubCommands: void 0
780
793
  };
781
794
  }
782
- if (isLazyCommand(cmd) && cmd.commandName == null) cmd.commandName = sub;
783
- else if (typeof cmd === "object" && cmd.name == null) cmd.name = sub;
795
+ if (positionals.length === 0 || options.subCommands.size === 0) return resolveAsEntry();
796
+ let currentSubCommands = options.subCommands;
797
+ let resolvedCommand;
798
+ let resolvedName;
799
+ const commandPath = [];
800
+ let depth = 0;
801
+ for (let i = 0; i < positionals.length; i++) {
802
+ const token = positionals[i];
803
+ const cmd = currentSubCommands.get(token);
804
+ if (cmd == null) {
805
+ if (depth === 0) {
806
+ if (options.fallbackToEntry) return resolveAsEntry();
807
+ return {
808
+ commandName: token,
809
+ callMode: "unexpected",
810
+ commandPath: [],
811
+ depth: 0,
812
+ omitted: false,
813
+ levelSubCommands: void 0
814
+ };
815
+ }
816
+ break;
817
+ }
818
+ let resolved = cmd;
819
+ if (typeof cmd === "function" && cmd.commandName == null) resolved = Object.assign((...args) => cmd(...args), cmd, { commandName: token });
820
+ else if (typeof cmd === "object" && cmd.name == null) resolved = Object.assign(create(), cmd, { name: token });
821
+ resolvedCommand = resolved;
822
+ resolvedName = token;
823
+ commandPath.push(token);
824
+ depth++;
825
+ const nestedSubCommands = getCommandSubCommands(cmd);
826
+ if (nestedSubCommands && nestedSubCommands.size > 0) currentSubCommands = nestedSubCommands;
827
+ else break;
828
+ }
829
+ if (!resolvedCommand) return resolveAsEntry();
830
+ const resolvedSubCommands = getCommandSubCommands(resolvedCommand);
831
+ const omitted = resolvedSubCommands != null && resolvedSubCommands.size > 0;
832
+ let levelSubCommands;
833
+ if (omitted && resolvedSubCommands) {
834
+ levelSubCommands = new Map(resolvedSubCommands);
835
+ let entryCopy;
836
+ if (typeof resolvedCommand === "function") entryCopy = Object.assign((...args) => resolvedCommand(...args), resolvedCommand, { entry: true });
837
+ else entryCopy = Object.assign(create(), resolvedCommand, { entry: true });
838
+ levelSubCommands.set(resolvedName || resolveEntryName(entryCopy), entryCopy);
839
+ }
784
840
  return {
785
- commandName: sub,
786
- command: cmd,
787
- callMode: "subCommand"
841
+ commandName: resolvedName,
842
+ command: resolvedCommand,
843
+ callMode: depth > 0 ? "subCommand" : "entry",
844
+ commandPath,
845
+ depth,
846
+ omitted,
847
+ levelSubCommands
788
848
  };
789
849
  }
790
850
  function resolveEntryName(entry) {
@@ -1,4 +1,4 @@
1
- import { C as GunshiParams, D as Prettify, S as ExtractExtensions, T as LazyCommand, g as CommandRunner, h as CommandLoader, i as Args, r as ArgValues, s as Command, t as ArgSchema, v as DefaultGunshiParams, w as GunshiParamsConstraint, x as ExtractArgs, y as ExtendContext } from "./types-RXstkMKh.js";
1
+ import { C as GunshiParams, D as Prettify, S as ExtractExtensions, T as LazyCommand, g as CommandRunner, h as CommandLoader, i as Args, r as ArgValues, s as Command, t as ArgSchema, v as DefaultGunshiParams, w as GunshiParamsConstraint, x as ExtractArgs, y as ExtendContext } from "./types-DSyU38G8.js";
2
2
  import { CommandContextParams, createCommandContext } from "./context.js";
3
3
 
4
4
  //#region src/definition.d.ts
package/lib/definition.js CHANGED
@@ -1,4 +1,4 @@
1
- import { t as createCommandContext } from "./context-2cGqZAlo.js";
1
+ import { t as createCommandContext } from "./context-CHDIbgwd.js";
2
2
 
3
3
  //#region src/definition.ts
4
4
  /**
@@ -63,6 +63,7 @@ function lazy(loader, definition) {
63
63
  lazyCommand.internal = definition.internal;
64
64
  lazyCommand.entry = definition.entry;
65
65
  lazyCommand.toKebab = definition.toKebab;
66
+ if (definition.subCommands) lazyCommand.subCommands = definition.subCommands;
66
67
  if ("resource" in definition) lazyCommand.resource = definition.resource;
67
68
  }
68
69
  return lazyCommand;
@@ -1,4 +1,4 @@
1
- import { T as LazyCommand, o as CliOptions, s as Command, v as DefaultGunshiParams, w as GunshiParamsConstraint } from "./types-RXstkMKh.js";
1
+ import { T as LazyCommand, o as CliOptions, s as Command, v as DefaultGunshiParams, w as GunshiParamsConstraint } from "./types-DSyU38G8.js";
2
2
 
3
3
  //#region src/generator.d.ts
4
4
 
@@ -18,6 +18,6 @@ type GenerateOptions<G extends GunshiParamsConstraint = DefaultGunshiParams> = C
18
18
  * @param options - A {@linkcode GenerateOptions | cli options}
19
19
  * @returns A rendered usage.
20
20
  */
21
- declare function generate<G extends GunshiParamsConstraint = DefaultGunshiParams>(command: string | null, entry: Command<G> | LazyCommand<G>, options?: GenerateOptions<G>): Promise<string>;
21
+ declare function generate<G extends GunshiParamsConstraint = DefaultGunshiParams>(command: string | string[] | null, entry: Command<G> | LazyCommand<G>, options?: GenerateOptions<G>): Promise<string>;
22
22
  //#endregion
23
23
  export { GenerateOptions, generate };
package/lib/generator.js CHANGED
@@ -1,7 +1,7 @@
1
- import "./core-BEHAY3pE.js";
2
- import { t as create } from "./utils-A01g1U8-.js";
3
- import "./src-DulE0ns5.js";
4
- import { t as cli } from "./cli-BzfnVXOV.js";
1
+ import "./core-C9P_Gd_f.js";
2
+ import { t as create } from "./utils-DNS9AIR_.js";
3
+ import "./src-CfO5ab2f.js";
4
+ import { t as cli } from "./cli-l4AYjN-O.js";
5
5
 
6
6
  //#region src/generator.ts
7
7
  /**
@@ -30,7 +30,10 @@ import { t as cli } from "./cli-BzfnVXOV.js";
30
30
  */
31
31
  async function generate(command, entry, options = {}) {
32
32
  const args = ["-h"];
33
- if (command != null) args.unshift(command);
33
+ if (command != null) {
34
+ const parts = Array.isArray(command) ? command : command.split(" ");
35
+ args.unshift(...parts);
36
+ }
34
37
  return await cli(args, entry, {
35
38
  ...create(),
36
39
  ...options,
package/lib/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- import { A as SubCommandable, B as plugin, C as GunshiParams, D as Prettify, E as NormalizeToGunshiParams, F as PluginExtension, I as PluginFunction, L as PluginOptions, M as OnPluginExtension, N as Plugin, O as RendererDecorator, P as PluginDependency, R as PluginWithExtension, S as ExtractExtensions, T as LazyCommand, V as PluginContext, _ as Commandable, a as Awaitable, b as ExtractArgExplicitlyProvided, c as CommandCallMode, d as CommandContextExtension, f as CommandDecorator, g as CommandRunner, h as CommandLoader, i as Args, j as ValidationErrorsDecorator, k as RenderingOptions, l as CommandContext, m as CommandExamplesFetcher, n as ArgToken, o as CliOptions, p as CommandEnvironment, r as ArgValues, s as Command, t as ArgSchema, u as CommandContextCore, v as DefaultGunshiParams, w as GunshiParamsConstraint, x as ExtractArgs, y as ExtendContext, z as PluginWithoutExtension } from "./types-RXstkMKh.js";
1
+ import { A as SubCommandable, B as plugin, C as GunshiParams, D as Prettify, E as NormalizeToGunshiParams, F as PluginExtension, I as PluginFunction, L as PluginOptions, M as OnPluginExtension, N as Plugin, O as RendererDecorator, P as PluginDependency, R as PluginWithExtension, S as ExtractExtensions, T as LazyCommand, V as PluginContext, _ as Commandable, a as Awaitable, b as ExtractArgExplicitlyProvided, c as CommandCallMode, d as CommandContextExtension, f as CommandDecorator, g as CommandRunner, h as CommandLoader, i as Args, j as ValidationErrorsDecorator, k as RenderingOptions, l as CommandContext, m as CommandExamplesFetcher, n as ArgToken, o as CliOptions, p as CommandEnvironment, r as ArgValues, s as Command, t as ArgSchema, u as CommandContextCore, v as DefaultGunshiParams, w as GunshiParamsConstraint, x as ExtractArgs, y as ExtendContext, z as PluginWithoutExtension } from "./types-DSyU38G8.js";
2
2
  import { CommandContextParams, createCommandContext } from "./context.js";
3
3
  import { define, defineWithTypes, lazy, lazyWithTypes } from "./definition.js";
4
- import { t as ANONYMOUS_COMMAND_NAME } from "./constants-BbfDN8vC.js";
4
+ import { t as ANONYMOUS_COMMAND_NAME } from "./constants-Cqazor4w.js";
5
5
  import { parseArgs, resolveArgs } from "args-tokens";
6
6
 
7
7
  //#region ../plugin-i18n/src/types.d.ts
package/lib/index.js CHANGED
@@ -1,10 +1,10 @@
1
- import { n as resolveArgs, r as parseArgs } from "./core-BEHAY3pE.js";
1
+ import { n as resolveArgs, r as parseArgs } from "./core-C9P_Gd_f.js";
2
2
  import { t as ANONYMOUS_COMMAND_NAME } from "./constants-WviiKqbt.js";
3
- import { t as createCommandContext } from "./context-2cGqZAlo.js";
3
+ import { t as createCommandContext } from "./context-CHDIbgwd.js";
4
4
  import { define, defineWithTypes, lazy, lazyWithTypes } from "./definition.js";
5
5
  import { t as plugin } from "./core-eN5xmB3s.js";
6
- import { a as namespacedId, o as BUILT_IN_PREFIX } from "./src-DulE0ns5.js";
7
- import { t as cli } from "./cli-BzfnVXOV.js";
6
+ import { a as namespacedId, o as BUILT_IN_PREFIX } from "./src-CfO5ab2f.js";
7
+ import { t as cli } from "./cli-l4AYjN-O.js";
8
8
 
9
9
  //#region ../plugin-i18n/src/translation.ts
10
10
  /**
package/lib/plugin.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { B as plugin, C as GunshiParams, D as Prettify, E as NormalizeToGunshiParams, F as PluginExtension, I as PluginFunction, L as PluginOptions, M as OnPluginExtension, N as Plugin, O as RendererDecorator, P as PluginDependency, R as PluginWithExtension, S as ExtractExtensions, T as LazyCommand, V as PluginContext, a as Awaitable, d as CommandContextExtension, f as CommandDecorator, g as CommandRunner, i as Args, j as ValidationErrorsDecorator, l as CommandContext, m as CommandExamplesFetcher, n as ArgToken, r as ArgValues, s as Command, t as ArgSchema, u as CommandContextCore, v as DefaultGunshiParams, w as GunshiParamsConstraint, x as ExtractArgs, y as ExtendContext, z as PluginWithoutExtension } from "./types-RXstkMKh.js";
1
+ import { B as plugin, C as GunshiParams, D as Prettify, E as NormalizeToGunshiParams, F as PluginExtension, I as PluginFunction, L as PluginOptions, M as OnPluginExtension, N as Plugin, O as RendererDecorator, P as PluginDependency, R as PluginWithExtension, S as ExtractExtensions, T as LazyCommand, V as PluginContext, a as Awaitable, d as CommandContextExtension, f as CommandDecorator, g as CommandRunner, i as Args, j as ValidationErrorsDecorator, l as CommandContext, m as CommandExamplesFetcher, n as ArgToken, r as ArgValues, s as Command, t as ArgSchema, u as CommandContextCore, v as DefaultGunshiParams, w as GunshiParamsConstraint, x as ExtractArgs, y as ExtendContext, z as PluginWithoutExtension } from "./types-DSyU38G8.js";
2
2
  import { CommandContextParams, createCommandContext } from "./context.js";
3
- import { n as CLI_OPTIONS_DEFAULT, t as ANONYMOUS_COMMAND_NAME } from "./constants-BbfDN8vC.js";
3
+ import { n as CLI_OPTIONS_DEFAULT, t as ANONYMOUS_COMMAND_NAME } from "./constants-Cqazor4w.js";
4
4
  export { ANONYMOUS_COMMAND_NAME, type ArgSchema, type ArgToken, type ArgValues, type Args, type Awaitable, CLI_OPTIONS_DEFAULT, type Command, type CommandContext, type CommandContextCore, type CommandContextExtension, type CommandContextParams, type CommandDecorator, type CommandExamplesFetcher, type CommandRunner, type DefaultGunshiParams, type ExtendContext, type ExtractArgs, type ExtractExtensions, type GunshiParams, type GunshiParamsConstraint, type LazyCommand, type NormalizeToGunshiParams, type OnPluginExtension, type Plugin, type PluginContext, type PluginDependency, type PluginExtension, type PluginFunction, type PluginOptions, type PluginWithExtension, type PluginWithoutExtension, type Prettify, type RendererDecorator, type ValidationErrorsDecorator, createCommandContext, plugin };
package/lib/plugin.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { n as CLI_OPTIONS_DEFAULT, t as ANONYMOUS_COMMAND_NAME } from "./constants-WviiKqbt.js";
2
- import { t as createCommandContext } from "./context-2cGqZAlo.js";
2
+ import { t as createCommandContext } from "./context-CHDIbgwd.js";
3
3
  import { t as plugin } from "./core-eN5xmB3s.js";
4
4
 
5
5
  export { ANONYMOUS_COMMAND_NAME, CLI_OPTIONS_DEFAULT, createCommandContext, plugin };
package/lib/renderer.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { C as GunshiParams, l as CommandContext, v as DefaultGunshiParams } from "./types-RXstkMKh.js";
1
+ import { C as GunshiParams, l as CommandContext, v as DefaultGunshiParams } from "./types-DSyU38G8.js";
2
2
 
3
3
  //#region ../plugin-renderer/src/header.d.ts
4
4
  /**
package/lib/renderer.js CHANGED
@@ -1,3 +1,3 @@
1
- import { i as renderHeader, n as renderValidationErrors, r as renderUsage } from "./src-DulE0ns5.js";
1
+ import { i as renderHeader, n as renderValidationErrors, r as renderUsage } from "./src-CfO5ab2f.js";
2
2
 
3
3
  export { renderHeader, renderUsage, renderValidationErrors };
@@ -1,4 +1,4 @@
1
- import { a as resolveLazyCommand, o as kebabnize } from "./utils-A01g1U8-.js";
1
+ import { o as resolveLazyCommand, s as kebabnize } from "./utils-DNS9AIR_.js";
2
2
  import { t as ANONYMOUS_COMMAND_NAME } from "./constants-WviiKqbt.js";
3
3
  import { t as plugin } from "./core-eN5xmB3s.js";
4
4
 
@@ -213,7 +213,7 @@ const COMMON_ARGS_KEYS = Object.keys(COMMON_ARGS);
213
213
  */
214
214
  async function renderUsage(ctx) {
215
215
  const messages = [];
216
- if (!ctx.omitted) {
216
+ if (ctx.callMode === "subCommand") {
217
217
  const description = await resolveDescription(ctx);
218
218
  if (description) messages.push(description, "");
219
219
  }
@@ -307,10 +307,11 @@ async function renderCommandsSection(ctx) {
307
307
  return `${command.padStart(ctx.env.leftMargin + command.length)}`;
308
308
  }));
309
309
  messages.push(...commandsStr, "", `${await ctx.extensions[pluginId].text(resolveBuiltInKey("FORMORE"))}:`);
310
+ const basePath = ctx.commandPath && ctx.commandPath.length > 0 ? `${ctx.env.name} ${ctx.commandPath.join(" ")}` : ctx.env.name;
310
311
  messages.push(...loadedCommands.map((cmd) => {
311
312
  let commandStr = cmd.entry ? "" : cmd.name || "";
312
313
  if (commandStr) commandStr += " ";
313
- const commandHelp = `${ctx.env.name} ${commandStr}--help`;
314
+ const commandHelp = `${basePath} ${commandStr}--help`;
314
315
  return `${commandHelp.padStart(ctx.env.leftMargin + commandHelp.length)}`;
315
316
  }));
316
317
  return messages;
@@ -345,6 +346,7 @@ async function resolveEntry(ctx) {
345
346
  * @returns The sub command name
346
347
  */
347
348
  async function resolveSubCommand(ctx) {
349
+ if (ctx.commandPath && ctx.commandPath.length > 0) return ctx.commandPath.join(" ");
348
350
  return ctx.name || await ctx.extensions[pluginId].text(resolveBuiltInKey("SUBCOMMAND"));
349
351
  }
350
352
  /**
@@ -718,6 +718,15 @@ interface CommandContext<G extends GunshiParamsConstraint = DefaultGunshiParams>
718
718
  * The command call mode is `entry` when the command is executed as an entry command, and `subCommand` when the command is executed as a sub-command.
719
719
  */
720
720
  callMode: CommandCallMode;
721
+ /**
722
+ * The path of nested sub-commands that were resolved to reach the current command.
723
+ *
724
+ * For example, if the user runs `git remote add`, `commandPath` would be `['remote', 'add']`.
725
+ * For the entry command, this is an empty array.
726
+ *
727
+ * @since v0.28.0
728
+ */
729
+ commandPath: string[];
721
730
  /**
722
731
  * Whether to convert the camel-case style argument name to kebab-case.
723
732
  * This context value is set from {@linkcode Command.toKebab} option.
@@ -859,6 +868,15 @@ interface Command<G extends GunshiParamsConstraint = DefaultGunshiParams> {
859
868
  * @since v0.27.0
860
869
  */
861
870
  rendering?: RenderingOptions<G>;
871
+ /**
872
+ * Nested sub-commands for this command.
873
+ *
874
+ * Allows building command trees like `git remote add`.
875
+ * Each key is the sub-command name, and the value is a command or lazy command.
876
+ *
877
+ * @since v0.28.0
878
+ */
879
+ subCommands?: Record<string, SubCommandable> | Map<string, SubCommandable>;
862
880
  }
863
881
  /**
864
882
  * Lazy command interface.
@@ -935,6 +953,13 @@ interface SubCommandable {
935
953
  * see {@link LazyCommand.commandName}
936
954
  */
937
955
  commandName?: string;
956
+ /**
957
+ * Nested sub-commands for this command.
958
+ *
959
+ * @see {@link Command.subCommands}
960
+ * @since v0.28.0
961
+ */
962
+ subCommands?: Record<string, any> | Map<string, any>;
938
963
  /**
939
964
  * Index signature to allow additional properties
940
965
  */
@@ -48,7 +48,8 @@ async function resolveLazyCommand(cmd, name, needRunResolving = false) {
48
48
  args: cmd.args,
49
49
  examples: cmd.examples,
50
50
  internal: cmd.internal,
51
- entry: cmd.entry
51
+ entry: cmd.entry,
52
+ subCommands: cmd.subCommands
52
53
  };
53
54
  if ("resource" in cmd && cmd.resource) baseCommand.resource = cmd.resource;
54
55
  command = Object.assign(create(), baseCommand);
@@ -64,6 +65,7 @@ async function resolveLazyCommand(cmd, name, needRunResolving = false) {
64
65
  command.examples = loaded.examples;
65
66
  command.internal = loaded.internal;
66
67
  command.entry = loaded.entry;
68
+ command.subCommands = loaded.subCommands || cmd.subCommands;
67
69
  if ("resource" in loaded && loaded.resource) command.resource = loaded.resource;
68
70
  } else throw new TypeError(`Cannot resolve command: ${cmd.name || name}`);
69
71
  }
@@ -89,6 +91,24 @@ function log(...args) {
89
91
  console.log(...args);
90
92
  }
91
93
  /**
94
+ * Get the sub-commands of a command as a normalized Map.
95
+ *
96
+ * @param cmd - A command or lazy command
97
+ * @returns A Map of sub-commands, or undefined if the command has no sub-commands.
98
+ */
99
+ function getCommandSubCommands(cmd) {
100
+ const subCommands = isLazyCommand(cmd) ? cmd.subCommands : typeof cmd === "object" ? cmd.subCommands : void 0;
101
+ if (!subCommands) return;
102
+ if (subCommands instanceof Map) return subCommands.size > 0 ? subCommands : void 0;
103
+ if (typeof subCommands === "object") {
104
+ const entries = Object.entries(subCommands);
105
+ if (entries.length === 0) return;
106
+ const map = /* @__PURE__ */ new Map();
107
+ for (const [name, cmd] of entries) map.set(name, cmd);
108
+ return map;
109
+ }
110
+ }
111
+ /**
92
112
  * Deep freeze an object, making it immutable.
93
113
  *
94
114
  * @param obj - The object to freeze
@@ -106,4 +126,4 @@ function deepFreeze(obj, ignores = []) {
106
126
  }
107
127
 
108
128
  //#endregion
109
- export { resolveLazyCommand as a, log as i, deepFreeze as n, kebabnize as o, isLazyCommand as r, create as t };
129
+ export { log as a, isLazyCommand as i, deepFreeze as n, resolveLazyCommand as o, getCommandSubCommands as r, kebabnize as s, create as t };
package/lib/utils.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { T as LazyCommand, _ as Commandable, s as Command, v as DefaultGunshiParams, w as GunshiParamsConstraint } from "./types-RXstkMKh.js";
1
+ import { T as LazyCommand, _ as Commandable, s as Command, v as DefaultGunshiParams, w as GunshiParamsConstraint } from "./types-DSyU38G8.js";
2
2
  import { kebabnize } from "args-tokens/utils";
3
3
 
4
4
  //#region src/utils.d.ts
@@ -36,6 +36,13 @@ declare function create<T>(obj?: object | null): T;
36
36
  * @param args - Arguments to log
37
37
  */
38
38
  declare function log(...args: unknown[]): void;
39
+ /**
40
+ * Get the sub-commands of a command as a normalized Map.
41
+ *
42
+ * @param cmd - A command or lazy command
43
+ * @returns A Map of sub-commands, or undefined if the command has no sub-commands.
44
+ */
45
+ declare function getCommandSubCommands<G extends GunshiParamsConstraint = DefaultGunshiParams>(cmd: Commandable<G> | Command<G> | LazyCommand<G>): Map<string, Command<G> | LazyCommand<G>> | undefined;
39
46
  /**
40
47
  * Deep freeze an object, making it immutable.
41
48
  *
@@ -45,4 +52,4 @@ declare function log(...args: unknown[]): void;
45
52
  */
46
53
  declare function deepFreeze<T extends Record<string, any>>(obj: T, ignores?: string[]): Readonly<T>;
47
54
  //#endregion
48
- export { create, deepFreeze, isLazyCommand, kebabnize, log, resolveLazyCommand };
55
+ export { create, deepFreeze, getCommandSubCommands, isLazyCommand, kebabnize, log, resolveLazyCommand };
package/lib/utils.js CHANGED
@@ -1,3 +1,3 @@
1
- import { a as resolveLazyCommand, i as log, n as deepFreeze, o as kebabnize, r as isLazyCommand, t as create } from "./utils-A01g1U8-.js";
1
+ import { a as log, i as isLazyCommand, n as deepFreeze, o as resolveLazyCommand, r as getCommandSubCommands, s as kebabnize, t as create } from "./utils-DNS9AIR_.js";
2
2
 
3
- export { create, deepFreeze, isLazyCommand, kebabnize, log, resolveLazyCommand };
3
+ export { create, deepFreeze, getCommandSubCommands, isLazyCommand, kebabnize, log, resolveLazyCommand };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "gunshi",
3
3
  "description": "Modern javascript command-line library",
4
- "version": "0.27.6",
4
+ "version": "0.28.0",
5
5
  "author": {
6
6
  "name": "kazuya kawaguchi",
7
7
  "email": "kawakazu80@gmail.com"
@@ -103,11 +103,11 @@
103
103
  "publint": "^0.3.16",
104
104
  "tsdown": "0.15.12",
105
105
  "zod": "^4.2.1",
106
- "@gunshi/plugin-global": "0.27.6",
107
- "@gunshi/plugin-renderer": "0.27.6",
108
- "@gunshi/resources": "0.27.6",
109
- "@gunshi/plugin-i18n": "0.27.6",
110
- "@gunshi/shared": "0.27.6"
106
+ "@gunshi/plugin-global": "0.28.0",
107
+ "@gunshi/plugin-i18n": "0.28.0",
108
+ "@gunshi/resources": "0.28.0",
109
+ "@gunshi/shared": "0.28.0",
110
+ "@gunshi/plugin-renderer": "0.28.0"
111
111
  },
112
112
  "scripts": {
113
113
  "build": "tsdown",