@bemoje/cli 1.0.6 → 1.1.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.
package/lib/Command.js CHANGED
@@ -1,7 +1,7 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
3
  import { parseArgs } from "node:util";
4
- import { CommandHelpAdapter } from "./CommandHelpAdapter.js";
4
+ import { Help } from "./Help.js";
5
5
  class Command {
6
6
  static {
7
7
  __name(this, "Command");
@@ -41,37 +41,6 @@ class Command {
41
41
  this.helpConfiguration = { showGlobalOptions: true, sortOptions: true, sortSubcommands: true };
42
42
  Object.defineProperty(this, "parent", { enumerable: false });
43
43
  }
44
- /** Updates multiple command properties at once */
45
- setState(state) {
46
- Object.assign(this, state);
47
- if (state.commands) {
48
- state.commands = state.commands.map((cmd) => new Command(cmd.name, this).setState(cmd));
49
- }
50
- return this;
51
- }
52
- /** Serializes command to JSON, maintaining compatibility with previous state-based structure */
53
- toJSON() {
54
- const result = {
55
- name: this.name,
56
- version: this.version,
57
- aliases: this.aliases,
58
- summary: this.summary,
59
- description: this.description,
60
- hidden: this.hidden,
61
- group: this.group,
62
- commands: this.commands,
63
- arguments: this.arguments,
64
- options: this.options,
65
- helpConfiguration: this.helpConfiguration
66
- };
67
- Object.defineProperty(result, "parent", {
68
- value: this.parent,
69
- writable: true,
70
- enumerable: false,
71
- configurable: true
72
- });
73
- return result;
74
- }
75
44
  /** Sets the command name */
76
45
  setName(name) {
77
46
  this.name = name;
@@ -96,7 +65,7 @@ class Command {
96
65
  this.summary = summary;
97
66
  return this;
98
67
  }
99
- /** Sets command description, joining multiple lines */
68
+ /** Sets command description, joining variadic lines */
100
69
  setDescription(...lines) {
101
70
  this.description = lines.join("\n");
102
71
  return this;
@@ -127,59 +96,56 @@ class Command {
127
96
  return this;
128
97
  }
129
98
  /** Creates and adds a subcommand */
130
- subcommand(name) {
131
- const sub = new Command(name, this);
99
+ addSubcommand(name) {
100
+ const sub = this.createCommand(name, this);
132
101
  this.commands.push(sub);
133
102
  return sub;
134
103
  }
135
104
  /**
136
105
  * Adds positional argument with type inference and CLI ordering validation.
137
106
  */
138
- argument(usage, description, options = {}) {
107
+ addArgument(usage, description, options = {}) {
139
108
  const match = usage.match(/^<(.*?)>$|^\[(.*?)\]$/);
140
109
  if (!match) throw new Error(`Invalid argument format: ${usage}`);
141
110
  const nameMatch = match[1] || match[2];
142
111
  const name = nameMatch.replace(/\.\.\.$/, "");
143
112
  this.assertArgumentNameNotInUse(name);
113
+ const props = { name, description };
144
114
  if (usage.startsWith("<")) {
145
115
  if (nameMatch.endsWith("...")) {
146
116
  this.assertNoMultipleVariadicArguments();
147
117
  this.arguments.push({
148
- name,
149
- description,
118
+ ...options,
119
+ ...props,
150
120
  required: true,
151
- multiple: true,
152
- ...options
121
+ variadic: true
153
122
  });
154
123
  } else {
155
124
  this.assertNoOptionalOrVariadicArguments();
156
125
  this.arguments.push({
157
- name,
158
- description,
126
+ ...options,
127
+ ...props,
159
128
  required: true,
160
- multiple: false,
161
- ...options
129
+ variadic: false
162
130
  });
163
131
  }
164
132
  } else if (usage.startsWith("[")) {
165
133
  if (nameMatch.endsWith("...")) {
166
134
  this.assertNoMultipleVariadicArguments();
167
135
  this.arguments.push({
168
- name,
169
- description,
136
+ ...options,
137
+ ...props,
170
138
  required: false,
171
- multiple: true,
172
- defaultValue: options.defaultValue ?? [],
173
- ...options
139
+ variadic: true,
140
+ defaultValue: options.defaultValue ?? []
174
141
  });
175
142
  } else {
176
143
  this.assertNoVariadicArgument();
177
144
  this.arguments.push({
178
- name,
179
- description,
145
+ ...options,
146
+ ...props,
180
147
  required: false,
181
- multiple: false,
182
- ...options
148
+ variadic: false
183
149
  });
184
150
  }
185
151
  }
@@ -188,77 +154,97 @@ class Command {
188
154
  /**
189
155
  * Adds command-line option with type inference. Parses format: `-s, --long [<value>|[value]|<value...>|[value...]]`
190
156
  */
191
- option(usage, description, opts = {}) {
192
- const match = usage.match(/^-(.+?), --([a-zA-Z][\w-]*)(?:\s*(<(.+?)>|\[(.+?)\]))?$/);
193
- if (!match) throw new Error(`Invalid option format: ${usage}`);
157
+ addOption(flags, description, opts = {}) {
158
+ const match = flags.match(/^-(.+?), --([a-zA-Z][\w-]*)(?:\s*(<(.+?)>|\[(.+?)\]))?$/);
159
+ if (!match) throw new Error(`Invalid option format: ${flags}`);
194
160
  const short = match[1];
195
161
  this.assertOptionShortNameIsValid(short);
196
162
  this.assertOptionShortNameNotInUse(short);
197
163
  const name = match[2];
198
164
  const argName = (match[4] || match[5])?.replace(/\.\.\.$/, "");
199
165
  this.assertOptionNameNotInUse(name);
166
+ const props = {
167
+ flags,
168
+ short,
169
+ long: name,
170
+ name,
171
+ description
172
+ };
200
173
  if (!argName) {
201
- this.options.push({
174
+ this._addOption({
202
175
  type: "boolean",
203
- short,
204
- name,
205
- description,
206
- required: false,
207
- multiple: false,
208
- ...opts
176
+ ...opts,
177
+ ...props,
178
+ negate: false,
179
+ optional: true,
180
+ variadic: false,
181
+ get multiple() {
182
+ return this.variadic;
183
+ }
209
184
  });
210
- } else if (usage.endsWith(">")) {
211
- if (usage.endsWith("...>")) {
212
- this.options.push({
185
+ } else if (flags.endsWith(">")) {
186
+ if (flags.endsWith("...>")) {
187
+ this._addOption({
213
188
  type: "string",
214
- short,
215
- name,
189
+ ...opts,
190
+ ...props,
216
191
  argName,
217
- description,
218
192
  required: true,
219
- multiple: true,
220
- ...opts
193
+ optional: false,
194
+ variadic: true,
195
+ get multiple() {
196
+ return this.variadic;
197
+ }
221
198
  });
222
199
  } else {
223
- this.options.push({
200
+ this._addOption({
224
201
  type: "string",
225
- short,
226
- name,
202
+ ...opts,
203
+ ...props,
227
204
  argName,
228
- description,
229
205
  required: true,
230
- multiple: false,
231
- ...opts
206
+ optional: false,
207
+ variadic: false,
208
+ get multiple() {
209
+ return this.variadic;
210
+ }
232
211
  });
233
212
  }
234
- } else if (usage.endsWith("]")) {
235
- if (usage.endsWith("...]")) {
236
- this.options.push({
213
+ } else if (flags.endsWith("]")) {
214
+ if (flags.endsWith("...]")) {
215
+ this._addOption({
237
216
  type: "string",
238
- short,
239
- name,
217
+ ...opts,
218
+ ...props,
240
219
  argName,
241
- description,
242
220
  required: false,
243
- multiple: true,
244
- defaultValue: opts.defaultValue ?? [],
245
- ...opts
221
+ optional: true,
222
+ variadic: true,
223
+ get multiple() {
224
+ return this.variadic;
225
+ },
226
+ defaultValue: opts.defaultValue ?? []
246
227
  });
247
228
  } else {
248
- this.options.push({
229
+ this._addOption({
249
230
  type: "string",
250
- short,
251
- name,
231
+ ...opts,
232
+ ...props,
252
233
  argName,
253
- description,
254
234
  required: false,
255
- multiple: false,
256
- ...opts
235
+ optional: true,
236
+ variadic: false,
237
+ get multiple() {
238
+ return this.variadic;
239
+ }
257
240
  });
258
241
  }
259
242
  }
260
243
  return this;
261
244
  }
245
+ _addOption(opt) {
246
+ this.options.push(opt);
247
+ }
262
248
  /**
263
249
  * Parses command-line arguments with subcommand support and type-safe validation.
264
250
  *
@@ -268,7 +254,7 @@ class Command {
268
254
  * // { arguments: ['input.txt'], options: { verbose: true, format: 'json' } }
269
255
  * ```
270
256
  */
271
- parse(argv = process.argv.slice(2), globalOptions = []) {
257
+ parseArgv(argv = process.argv.slice(2), globalOptions = []) {
272
258
  const maybeSubArg = parseArgs({
273
259
  args: argv,
274
260
  allowPositionals: true,
@@ -278,7 +264,7 @@ class Command {
278
264
  }).positionals[0];
279
265
  const sub = this.findCommand(maybeSubArg);
280
266
  if (sub) {
281
- return sub.parse(
267
+ return sub.parseArgv(
282
268
  argv?.filter((a) => a !== maybeSubArg),
283
269
  [...globalOptions, ...this.options]
284
270
  );
@@ -299,7 +285,7 @@ class Command {
299
285
  const token = parsed.tokens[i];
300
286
  if (token.kind === "option") {
301
287
  const optionDescriptor = this.options.find((o) => o.name === token.name);
302
- if (optionDescriptor && optionDescriptor.multiple && optionDescriptor.type === "string") {
288
+ if (optionDescriptor && optionDescriptor.variadic && optionDescriptor.type === "string") {
303
289
  const values = [token.value];
304
290
  let j = i + 1;
305
291
  while (j < parsed.tokens.length && parsed.tokens[j].kind === "positional") {
@@ -322,13 +308,13 @@ class Command {
322
308
  }
323
309
  }
324
310
  const parsedArguments = this.arguments.map((arg, index) => {
325
- if (arg.multiple) {
311
+ if (arg.variadic) {
326
312
  const remainingArgs = parsed.positionals.slice(index);
327
313
  return remainingArgs.length > 0 ? remainingArgs : arg.defaultValue ?? [];
328
314
  } else {
329
315
  return parsed.positionals[index] ?? arg.defaultValue;
330
316
  }
331
- });
317
+ }).filter((arg) => arg !== void 0);
332
318
  for (const option of this.options) {
333
319
  if (!(option.name in parsed.values) && "defaultValue" in option) {
334
320
  Reflect.set(parsed.values, option.name, option.defaultValue);
@@ -343,6 +329,11 @@ class Command {
343
329
  options: { ...parsed.values }
344
330
  };
345
331
  }
332
+ /** Renders formatted help text using provided help definition */
333
+ renderHelp(help = new Help()) {
334
+ const helper = Object.assign(help, this.helpConfiguration);
335
+ return helper.formatHelp(this, helper);
336
+ }
346
337
  /** Validates CLI argument ordering */
347
338
  assertNoOptionalOrVariadicArguments() {
348
339
  if (this.arguments.some((arg) => !arg.required)) {
@@ -351,13 +342,13 @@ class Command {
351
342
  }
352
343
  /** Validates optional args don't follow variadic args */
353
344
  assertNoVariadicArgument() {
354
- if (this.arguments.some((arg) => arg.multiple)) {
345
+ if (this.arguments.some((arg) => arg.variadic)) {
355
346
  throw new Error("Cannot add optional argument after variadic argument");
356
347
  }
357
348
  }
358
349
  /** Ensures only one variadic argument per command */
359
350
  assertNoMultipleVariadicArguments() {
360
- if (this.arguments.some((arg) => arg.multiple)) {
351
+ if (this.arguments.some((arg) => arg.variadic)) {
361
352
  throw new Error("Cannot add more than one variadic argument");
362
353
  }
363
354
  }
@@ -419,13 +410,9 @@ class Command {
419
410
  findOption(arg) {
420
411
  return this.options.find((option) => option.short === arg || option.name === arg);
421
412
  }
422
- /** Returns a view that is compliant with the CommandHelp interface */
423
- createHelpAdapter() {
424
- return new CommandHelpAdapter(this);
425
- }
426
- /** Renders formatted help text using provided help definition */
427
- renderHelp(help) {
428
- return this.createHelpAdapter().renderHelp(help);
413
+ /** Returns a new Command instance. Override this method in subclasses. */
414
+ createCommand(name = "", parent = null) {
415
+ return new Command(name, parent);
429
416
  }
430
417
  }
431
418
  export {
package/lib/Help.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import type { ArgumentDescriptorBase, CommandDescriptor, OptionDescriptorBase } from './Command';
1
2
  /**
2
3
  * Although this is a class, methods are static in style to allow override using subclass or just functions.
3
4
  */
@@ -11,72 +12,72 @@ export declare class Help implements IHelp {
11
12
  /**
12
13
  * Get an array of the visible subcommands. Includes a placeholder for the implicit help command, if there is one.
13
14
  */
14
- visibleCommands(cmd: ICommandHelp): ICommandHelp[];
15
+ visibleCommands(cmd: CommandDescriptor): CommandDescriptor[];
15
16
  /**
16
17
  * Compare options for sort.
17
18
  */
18
- compareOptions(a: IOptionHelp, b: IOptionHelp): number;
19
+ compareOptions(a: OptionDescriptorBase, b: OptionDescriptorBase): number;
19
20
  /**
20
21
  * Get an array of the visible options. Includes a placeholder for the implicit help option, if there is one.
21
22
  */
22
- visibleOptions(cmd: ICommandHelp): IOptionHelp[];
23
+ visibleOptions(cmd: CommandDescriptor): OptionDescriptorBase[];
23
24
  /**
24
25
  * Get an array of the visible global options. (Not including help.)
25
26
  */
26
- visibleGlobalOptions(cmd: ICommandHelp): IOptionHelp[];
27
+ visibleGlobalOptions(cmd: CommandDescriptor): OptionDescriptorBase[];
27
28
  /**
28
29
  * Get an array of the arguments if any have a description.
29
30
  */
30
- visibleArguments(cmd: ICommandHelp): IArgumentHelp[];
31
+ visibleArguments(cmd: CommandDescriptor): ArgumentDescriptorBase[];
31
32
  /**
32
33
  * Get the command term to show in the list of subcommands.
33
34
  */
34
- subcommandTerm(cmd: ICommandHelp): string;
35
+ subcommandTerm(cmd: CommandDescriptor): string;
35
36
  /**
36
37
  * Get the option term to show in the list of options.
37
38
  */
38
- optionTerm(option: IOptionHelp): string;
39
+ optionTerm(option: OptionDescriptorBase): string;
39
40
  /**
40
41
  * Get the argument term to show in the list of arguments.
41
42
  */
42
- argumentTerm(argument: IArgumentHelp): string;
43
+ argumentTerm(argument: ArgumentDescriptorBase): string;
43
44
  /**
44
45
  * Get the longest command term length.
45
46
  */
46
- longestSubcommandTermLength(cmd: ICommandHelp, helper: Help): number;
47
+ longestSubcommandTermLength(cmd: CommandDescriptor, helper: Help): number;
47
48
  /**
48
49
  * Get the longest option term length.
49
50
  */
50
- longestOptionTermLength(cmd: ICommandHelp, helper: Help): number;
51
+ longestOptionTermLength(cmd: CommandDescriptor, helper: Help): number;
51
52
  /**
52
53
  * Get the longest global option term length.
53
54
  */
54
- longestGlobalOptionTermLength(cmd: ICommandHelp, helper: Help): number;
55
+ longestGlobalOptionTermLength(cmd: CommandDescriptor, helper: Help): number;
55
56
  /**
56
57
  * Get the longest argument term length.
57
58
  */
58
- longestArgumentTermLength(cmd: ICommandHelp, helper: Help): number;
59
+ longestArgumentTermLength(cmd: CommandDescriptor, helper: Help): number;
59
60
  /**
60
61
  * Get the command usage to be displayed at the top of the built-in help.
61
62
  */
62
- commandUsage(cmd: ICommandHelp): string;
63
+ commandUsage(cmd: CommandDescriptor): string;
63
64
  /**
64
65
  * Get the description for the command.
65
66
  */
66
- commandDescription(cmd: ICommandHelp): string;
67
+ commandDescription(cmd: CommandDescriptor): string;
67
68
  /**
68
69
  * Get the subcommand summary to show in the list of subcommands.
69
70
  * (Fallback to description for backwards compatibility.)
70
71
  */
71
- subcommandDescription(cmd: ICommandHelp): string;
72
+ subcommandDescription(cmd: CommandDescriptor): string;
72
73
  /**
73
74
  * Get the option description to show in the list of options.
74
75
  */
75
- optionDescription(option: IOptionHelp): string;
76
+ optionDescription(option: OptionDescriptorBase): string;
76
77
  /**
77
78
  * Get the argument description to show in the list of arguments.
78
79
  */
79
- argumentDescription(argument: IArgumentHelp): string;
80
+ argumentDescription(argument: ArgumentDescriptorBase): string;
80
81
  /**
81
82
  * Format a list of items, given a heading and an array of formatted items.
82
83
  */
@@ -84,11 +85,11 @@ export declare class Help implements IHelp {
84
85
  /**
85
86
  * Group items by their help group heading.
86
87
  */
87
- groupItems<T extends ICommandHelp | IOptionHelp>(unsortedItems: T[], visibleItems: T[], getGroup: (item: T) => string): Map<string, T[]>;
88
+ groupItems<T extends CommandDescriptor | OptionDescriptorBase>(unsortedItems: T[], visibleItems: T[], getGroup: (item: T) => string): Map<string, T[]>;
88
89
  /**
89
90
  * Generate the built-in help text.
90
91
  */
91
- formatHelp(cmd: ICommandHelp, helper: IHelp): string;
92
+ formatHelp(cmd: CommandDescriptor, helper: IHelp): string;
92
93
  /**
93
94
  * Return display width of string, ignoring ANSI escape sequences. Used in padding and wrapping calculations.
94
95
  */
@@ -152,7 +153,7 @@ export declare class Help implements IHelp {
152
153
  /**
153
154
  * Calculate the pad width from the maximum term length.
154
155
  */
155
- padWidth(cmd: ICommandHelp, helper: Help): number;
156
+ padWidth(cmd: CommandDescriptor, helper: Help): number;
156
157
  /**
157
158
  * Detect manually wrapped and indented strings by checking for line break followed by whitespace.
158
159
  */
@@ -171,71 +172,28 @@ export declare class Help implements IHelp {
171
172
  */
172
173
  boxWrap(str: string, width: number): string;
173
174
  }
174
- /**
175
- * Interface for a command object used to generate help.
176
- */
177
- export interface ICommandHelp {
178
- name: string;
179
- aliases: string[];
180
- summary?: string;
181
- description: string;
182
- hidden?: boolean;
183
- usage: string;
184
- group?: string;
185
- commands: ICommandHelp[];
186
- options: IOptionHelp[];
187
- arguments: IArgumentHelp[];
188
- parent: ICommandHelp | null;
189
- helpConfiguration?: Partial<IHelp>;
190
- }
191
- export interface IArgumentHelp {
192
- name: string;
193
- description: string;
194
- required: boolean;
195
- variadic: boolean;
196
- defaultValue?: string | string[];
197
- defaultValueDescription?: string;
198
- choices?: string[];
199
- }
200
- export interface IOptionHelp {
201
- group?: string;
202
- flags: string;
203
- description: string;
204
- short: string;
205
- long: string;
206
- required?: boolean;
207
- optional?: boolean;
208
- variadic?: boolean;
209
- negate?: boolean;
210
- defaultValue?: boolean | string | string[];
211
- defaultValueDescription?: string;
212
- env?: string;
213
- choices?: string[];
214
- hidden?: boolean;
215
- global?: boolean;
216
- }
217
175
  export interface IHelp {
218
176
  helpWidth: number;
219
177
  minWidthToWrap: number;
220
178
  sortSubcommands?: boolean;
221
179
  sortOptions?: boolean;
222
180
  showGlobalOptions?: boolean;
223
- subcommandTerm(cmd: ICommandHelp): string;
224
- subcommandDescription(cmd: ICommandHelp): string;
225
- optionTerm(option: IOptionHelp): string;
226
- optionDescription(option: IOptionHelp): string;
227
- argumentTerm(argument: IArgumentHelp): string;
228
- argumentDescription(argument: IArgumentHelp): string;
229
- commandUsage(cmd: ICommandHelp): string;
230
- commandDescription(cmd: ICommandHelp): string;
231
- visibleCommands(cmd: ICommandHelp): ICommandHelp[];
232
- visibleOptions(cmd: ICommandHelp): IOptionHelp[];
233
- visibleGlobalOptions(cmd: ICommandHelp): IOptionHelp[];
234
- visibleArguments(cmd: ICommandHelp): IArgumentHelp[];
235
- longestSubcommandTermLength(cmd: ICommandHelp, helper: IHelp): number;
236
- longestOptionTermLength(cmd: ICommandHelp, helper: IHelp): number;
237
- longestGlobalOptionTermLength(cmd: ICommandHelp, helper: IHelp): number;
238
- longestArgumentTermLength(cmd: ICommandHelp, helper: IHelp): number;
181
+ subcommandTerm(cmd: CommandDescriptor): string;
182
+ subcommandDescription(cmd: CommandDescriptor): string;
183
+ optionTerm(option: OptionDescriptorBase): string;
184
+ optionDescription(option: OptionDescriptorBase): string;
185
+ argumentTerm(argument: ArgumentDescriptorBase): string;
186
+ argumentDescription(argument: ArgumentDescriptorBase): string;
187
+ commandUsage(cmd: CommandDescriptor): string;
188
+ commandDescription(cmd: CommandDescriptor): string;
189
+ visibleCommands(cmd: CommandDescriptor): CommandDescriptor[];
190
+ visibleOptions(cmd: CommandDescriptor): OptionDescriptorBase[];
191
+ visibleGlobalOptions(cmd: CommandDescriptor): OptionDescriptorBase[];
192
+ visibleArguments(cmd: CommandDescriptor): ArgumentDescriptorBase[];
193
+ longestSubcommandTermLength(cmd: CommandDescriptor, helper: IHelp): number;
194
+ longestOptionTermLength(cmd: CommandDescriptor, helper: IHelp): number;
195
+ longestGlobalOptionTermLength(cmd: CommandDescriptor, helper: IHelp): number;
196
+ longestArgumentTermLength(cmd: CommandDescriptor, helper: IHelp): number;
239
197
  displayWidth(str: string): number;
240
198
  styleTitle(title: string): string;
241
199
  styleUsage(str: string): string;
@@ -251,13 +209,13 @@ export interface IHelp {
251
209
  styleOptionText(str: string): string;
252
210
  styleSubcommandText(str: string): string;
253
211
  styleArgumentText(str: string): string;
254
- compareOptions(a: IOptionHelp, b: IOptionHelp): number;
255
- padWidth(cmd: ICommandHelp, helper: IHelp): number;
212
+ compareOptions(a: OptionDescriptorBase, b: OptionDescriptorBase): number;
213
+ padWidth(cmd: CommandDescriptor, helper: IHelp): number;
256
214
  boxWrap(str: string, width: number): string;
257
215
  preformatted(str: string): boolean;
258
216
  formatItem(term: string, termWidth: number, description: string, helper: IHelp): string;
259
217
  formatItemList(heading: string, items: string[], helper: IHelp): string[];
260
- groupItems<T extends ICommandHelp | IOptionHelp>(unsortedItems: T[], visibleItems: T[], getGroup: (item: T) => string): Map<string, T[]>;
261
- formatHelp(cmd: ICommandHelp, helper: IHelp): string;
218
+ groupItems<T extends CommandDescriptor | OptionDescriptorBase>(unsortedItems: T[], visibleItems: T[], getGroup: (item: T) => string): Map<string, T[]>;
219
+ formatHelp(cmd: CommandDescriptor, helper: IHelp): string;
262
220
  }
263
221
  //# sourceMappingURL=Help.d.ts.map
package/lib/Help.js CHANGED
@@ -69,7 +69,10 @@ class Help {
69
69
  * Get the command term to show in the list of subcommands.
70
70
  */
71
71
  subcommandTerm(cmd) {
72
- const args = cmd.arguments.map((arg) => humanReadableArgName(arg)).join(" ");
72
+ const args = cmd.arguments.map((arg) => {
73
+ const nameOutput = arg.name + (arg.variadic === true ? "..." : "");
74
+ return arg.required ? "<" + nameOutput + ">" : "[" + nameOutput + "]";
75
+ }).join(" ");
73
76
  return cmd.name + (cmd.aliases[0] ? "|" + cmd.aliases[0] : "") + (cmd.options.length ? " [options]" : "") + // simplistic check for non-help option
74
77
  (args ? " " + args : "");
75
78
  }
@@ -129,7 +132,13 @@ class Help {
129
132
  for (let ancestorCmd = cmd.parent; ancestorCmd; ancestorCmd = ancestorCmd.parent) {
130
133
  ancestorCmdNames = ancestorCmd.name + " " + ancestorCmdNames;
131
134
  }
132
- return ancestorCmdNames + cmdName + " " + cmd.usage;
135
+ return ancestorCmdNames + cmdName + " " + [
136
+ ...cmd.options.length ? ["[options]"] : [],
137
+ ...cmd.commands.length ? ["[command]"] : [],
138
+ ...cmd.arguments.map((arg) => {
139
+ return arg.required ? arg.variadic ? `<${arg.name}...>` : `<${arg.name}>` : arg.variadic ? `[${arg.name}...]` : `[${arg.name}]`;
140
+ })
141
+ ].join(" ");
133
142
  }
134
143
  /**
135
144
  * Get the description for the command.
@@ -142,7 +151,7 @@ class Help {
142
151
  * (Fallback to description for backwards compatibility.)
143
152
  */
144
153
  subcommandDescription(cmd) {
145
- return cmd.summary || cmd.description;
154
+ return cmd.summary || (cmd.description.includes("\n") ? cmd.description.split("\n")[0] : "");
146
155
  }
147
156
  /**
148
157
  * Get the option description to show in the list of options.
@@ -287,7 +296,8 @@ class Help {
287
296
  * Return display width of string, ignoring ANSI escape sequences. Used in padding and wrapping calculations.
288
297
  */
289
298
  displayWidth(str) {
290
- return stripColor(str).length;
299
+ const sgrPattern = /\x1b\[\d*(;\d*)*m/g;
300
+ return str.replace(sgrPattern, "").length;
291
301
  }
292
302
  /**
293
303
  * Style the title for displaying in the help. Called with 'Usage:', 'Options:', etc.
@@ -458,16 +468,6 @@ ${itemIndentStr}`);
458
468
  return wrappedLines.join("\n");
459
469
  }
460
470
  }
461
- function stripColor(str) {
462
- const sgrPattern = /\x1b\[\d*(;\d*)*m/g;
463
- return str.replace(sgrPattern, "");
464
- }
465
- __name(stripColor, "stripColor");
466
- function humanReadableArgName(arg) {
467
- const nameOutput = arg.name + (arg.variadic === true ? "..." : "");
468
- return arg.required ? "<" + nameOutput + ">" : "[" + nameOutput + "]";
469
- }
470
- __name(humanReadableArgName, "humanReadableArgName");
471
471
  export {
472
472
  Help
473
473
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@bemoje/cli",
3
- "description": "A type-safe CLI builder focused on command composition and help generation without execution coupling. Parse arguments, generate help, and integrate with existing CLI frameworks through a clean, fluent API.",
4
- "version": "1.0.6",
3
+ "description": "A type-safe CLI composer that can parse argv and generate help without execution coupling.",
4
+ "version": "1.1.1",
5
5
  "private": false,
6
6
  "sideEffects": false,
7
7
  "type": "module",
@@ -16,7 +16,8 @@
16
16
  "typings": "./index.d.ts",
17
17
  "dependencies": {},
18
18
  "devDependencies": {
19
- "commander": "^14.0.0"
19
+ "commander": "^14.0.0",
20
+ "type-fest": "4.41.0"
20
21
  },
21
22
  "license": "MIT",
22
23
  "repository": {
@@ -26,11 +27,8 @@
26
27
  "keywords": [
27
28
  "cli",
28
29
  "command-line",
29
- "argument-parser",
30
- "options-parser",
31
30
  "typescript",
32
31
  "type-safe",
33
- "fluent-api",
34
32
  "help-generation",
35
33
  "commander",
36
34
  "commander.js",
@@ -38,7 +36,6 @@
38
36
  "validation",
39
37
  "parsing",
40
38
  "composition",
41
- "adapter",
42
39
  "terminal",
43
40
  "console",
44
41
  "args",