padrone 1.1.0 → 1.2.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 (80) hide show
  1. package/CHANGELOG.md +38 -1
  2. package/LICENSE +1 -1
  3. package/README.md +60 -30
  4. package/dist/args-CKNh7Dm9.mjs +175 -0
  5. package/dist/args-CKNh7Dm9.mjs.map +1 -0
  6. package/dist/chunk-y_GBKt04.mjs +5 -0
  7. package/dist/codegen/index.d.mts +305 -0
  8. package/dist/codegen/index.d.mts.map +1 -0
  9. package/dist/codegen/index.mjs +1348 -0
  10. package/dist/codegen/index.mjs.map +1 -0
  11. package/dist/completion.d.mts +64 -0
  12. package/dist/completion.d.mts.map +1 -0
  13. package/dist/completion.mjs +417 -0
  14. package/dist/completion.mjs.map +1 -0
  15. package/dist/docs/index.d.mts +34 -0
  16. package/dist/docs/index.d.mts.map +1 -0
  17. package/dist/docs/index.mjs +404 -0
  18. package/dist/docs/index.mjs.map +1 -0
  19. package/dist/formatter-Dvx7jFXr.d.mts +82 -0
  20. package/dist/formatter-Dvx7jFXr.d.mts.map +1 -0
  21. package/dist/help-mUIX0T0V.mjs +1195 -0
  22. package/dist/help-mUIX0T0V.mjs.map +1 -0
  23. package/dist/index.d.mts +120 -546
  24. package/dist/index.d.mts.map +1 -1
  25. package/dist/index.mjs +1180 -1197
  26. package/dist/index.mjs.map +1 -1
  27. package/dist/test.d.mts +112 -0
  28. package/dist/test.d.mts.map +1 -0
  29. package/dist/test.mjs +138 -0
  30. package/dist/test.mjs.map +1 -0
  31. package/dist/types-qrtt0135.d.mts +1037 -0
  32. package/dist/types-qrtt0135.d.mts.map +1 -0
  33. package/dist/update-check-EbNDkzyV.mjs +146 -0
  34. package/dist/update-check-EbNDkzyV.mjs.map +1 -0
  35. package/package.json +61 -21
  36. package/src/args.ts +365 -0
  37. package/src/cli/completions.ts +29 -0
  38. package/src/cli/docs.ts +86 -0
  39. package/src/cli/doctor.ts +312 -0
  40. package/src/cli/index.ts +159 -0
  41. package/src/cli/init.ts +135 -0
  42. package/src/cli/link.ts +320 -0
  43. package/src/cli/wrap.ts +152 -0
  44. package/src/codegen/README.md +118 -0
  45. package/src/codegen/code-builder.ts +226 -0
  46. package/src/codegen/discovery.ts +232 -0
  47. package/src/codegen/file-emitter.ts +73 -0
  48. package/src/codegen/generators/barrel-file.ts +16 -0
  49. package/src/codegen/generators/command-file.ts +184 -0
  50. package/src/codegen/generators/command-tree.ts +124 -0
  51. package/src/codegen/index.ts +33 -0
  52. package/src/codegen/parsers/fish.ts +163 -0
  53. package/src/codegen/parsers/help.ts +378 -0
  54. package/src/codegen/parsers/merge.ts +158 -0
  55. package/src/codegen/parsers/zsh.ts +221 -0
  56. package/src/codegen/schema-to-code.ts +199 -0
  57. package/src/codegen/template.ts +69 -0
  58. package/src/codegen/types.ts +143 -0
  59. package/src/colorizer.ts +2 -2
  60. package/src/command-utils.ts +501 -0
  61. package/src/completion.ts +110 -97
  62. package/src/create.ts +1036 -305
  63. package/src/docs/index.ts +607 -0
  64. package/src/errors.ts +131 -0
  65. package/src/formatter.ts +149 -63
  66. package/src/help.ts +151 -55
  67. package/src/index.ts +12 -15
  68. package/src/interactive.ts +169 -0
  69. package/src/parse.ts +31 -16
  70. package/src/repl-loop.ts +317 -0
  71. package/src/runtime.ts +304 -0
  72. package/src/shell-utils.ts +83 -0
  73. package/src/test.ts +285 -0
  74. package/src/type-helpers.ts +10 -10
  75. package/src/type-utils.ts +124 -14
  76. package/src/types.ts +752 -154
  77. package/src/update-check.ts +244 -0
  78. package/src/wrap.ts +44 -40
  79. package/src/zod.d.ts +2 -2
  80. package/src/options.ts +0 -180
@@ -0,0 +1,184 @@
1
+ import { fieldMetaToCode } from '../schema-to-code.ts';
2
+ import type { CodeBuilder, CommandMeta, FieldMeta, GeneratorContext } from '../types.ts';
3
+
4
+ const JS_RESERVED = new Set([
5
+ 'break',
6
+ 'case',
7
+ 'catch',
8
+ 'continue',
9
+ 'debugger',
10
+ 'default',
11
+ 'delete',
12
+ 'do',
13
+ 'else',
14
+ 'export',
15
+ 'extends',
16
+ 'finally',
17
+ 'for',
18
+ 'function',
19
+ 'if',
20
+ 'import',
21
+ 'in',
22
+ 'instanceof',
23
+ 'new',
24
+ 'return',
25
+ 'super',
26
+ 'switch',
27
+ 'this',
28
+ 'throw',
29
+ 'try',
30
+ 'typeof',
31
+ 'var',
32
+ 'void',
33
+ 'while',
34
+ 'with',
35
+ 'yield',
36
+ 'class',
37
+ 'const',
38
+ 'enum',
39
+ 'let',
40
+ 'static',
41
+ 'implements',
42
+ 'interface',
43
+ 'package',
44
+ 'private',
45
+ 'protected',
46
+ 'public',
47
+ 'await',
48
+ 'async',
49
+ ]);
50
+
51
+ /** Convert a command name to a safe JS identifier (camelCase, reserved-word-safe). */
52
+ export function toSafeIdentifier(name: string): string {
53
+ const camel = name.replace(/-([a-z])/g, (_, c: string) => c.toUpperCase());
54
+ if (/^\d/.test(camel)) return `_${camel}`;
55
+ if (JS_RESERVED.has(camel)) return `_${camel}`;
56
+ return camel;
57
+ }
58
+
59
+ /** Build the exported function name for a command (e.g. 'repo' → 'repoCommand'). */
60
+ export function toCommandFunctionName(name: string): string {
61
+ return `${toSafeIdentifier(name)}Command`;
62
+ }
63
+
64
+ export interface CommandFileOptions {
65
+ /** Wrap config: generates .wrap() instead of .action(). */
66
+ wrap?: {
67
+ /** The external command to wrap (e.g. 'gh'). */
68
+ command: string;
69
+ /** Fixed args preceding the options (e.g. ['pr', 'list']). */
70
+ args?: string[];
71
+ };
72
+ /** Subcommand references to wire into this command via .command() calls. */
73
+ subcommands?: { name: string; varName: string; importPath: string; aliases?: string[] }[];
74
+ }
75
+
76
+ /**
77
+ * Generate a single Padrone command file from a CommandMeta.
78
+ * Produces a named function that chains .configure(), .arguments(), and .wrap() or .action().
79
+ */
80
+ export function generateCommandFile(command: CommandMeta, ctx: GeneratorContext, options?: CommandFileOptions): CodeBuilder {
81
+ const code = ctx.createCodeBuilder();
82
+
83
+ const hasArgs = (command.arguments && command.arguments.length > 0) || (command.positionals && command.positionals.length > 0);
84
+
85
+ if (hasArgs) {
86
+ code.import('z', 'zod/v4');
87
+ }
88
+
89
+ code.importType('AnyPadroneBuilder', 'padrone');
90
+
91
+ // Import subcommand modules
92
+ if (options?.subcommands) {
93
+ for (const sub of options.subcommands) {
94
+ code.import([sub.varName], sub.importPath);
95
+ }
96
+ }
97
+
98
+ code.line();
99
+
100
+ if (command.deprecated) {
101
+ const msg = typeof command.deprecated === 'string' ? command.deprecated : 'This command is deprecated';
102
+ code.comment(`@deprecated ${msg}`);
103
+ }
104
+
105
+ const fnName = toCommandFunctionName(command.name);
106
+ code.line(`export function ${fnName}<T extends AnyPadroneBuilder>(cmd: T) {`);
107
+ code.line(` return cmd`);
108
+
109
+ // .configure()
110
+ const configParts: string[] = [];
111
+ if (command.description) {
112
+ configParts.push(`description: ${JSON.stringify(command.description)}`);
113
+ }
114
+ if (command.deprecated) {
115
+ configParts.push(`deprecated: ${typeof command.deprecated === 'string' ? JSON.stringify(command.deprecated) : 'true'}`);
116
+ }
117
+ if (configParts.length > 0) {
118
+ code.line(` .configure({ ${configParts.join(', ')} })`);
119
+ }
120
+
121
+ // .arguments()
122
+ if (hasArgs) {
123
+ const allFields = [...(command.arguments || []), ...(command.positionals || [])];
124
+ const schemaCode = fieldMetaToCode(allFields);
125
+
126
+ const positionalNames = (command.positionals || []).map((p) => (p.type === 'array' ? `'...${p.name}'` : `'${p.name}'`));
127
+ const fieldsMap = buildFieldsMap(allFields);
128
+ const hasMetaOptions = positionalNames.length > 0 || fieldsMap;
129
+
130
+ if (hasMetaOptions) {
131
+ code.line(` .arguments(${schemaCode.code}, {`);
132
+ if (positionalNames.length > 0) {
133
+ code.line(` positional: [${positionalNames.join(', ')}],`);
134
+ }
135
+ if (fieldsMap) {
136
+ code.line(` fields: ${fieldsMap},`);
137
+ }
138
+ code.line(` })`);
139
+ } else {
140
+ code.line(` .arguments(${schemaCode.code})`);
141
+ }
142
+ }
143
+
144
+ // .command() calls for subcommands
145
+ if (options?.subcommands) {
146
+ for (const sub of options.subcommands) {
147
+ const nameArg =
148
+ sub.aliases && sub.aliases.length > 0
149
+ ? `[${JSON.stringify(sub.name)}, ${sub.aliases.map((a) => JSON.stringify(a)).join(', ')}]`
150
+ : JSON.stringify(sub.name);
151
+ code.line(` .command(${nameArg}, ${sub.varName})`);
152
+ }
153
+ }
154
+
155
+ // .wrap() or .action()
156
+ if (options?.wrap) {
157
+ const wrapParts: string[] = [];
158
+ wrapParts.push(`command: ${JSON.stringify(options.wrap.command)}`);
159
+ if (options.wrap.args && options.wrap.args.length > 0) {
160
+ wrapParts.push(`args: [${options.wrap.args.map((a) => JSON.stringify(a)).join(', ')}]`);
161
+ }
162
+ code.line(` .wrap({ ${wrapParts.join(', ')} })`);
163
+ } else {
164
+ code.line(` .action((args) => { /* TODO */ })`);
165
+ }
166
+
167
+ code.line(`}`);
168
+
169
+ return code;
170
+ }
171
+
172
+ function buildFieldsMap(fields: FieldMeta[]): string | null {
173
+ const entries: string[] = [];
174
+ for (const field of fields) {
175
+ if (field.aliases && field.aliases.length > 0) {
176
+ const alias =
177
+ field.aliases.length === 1 ? JSON.stringify(field.aliases[0]) : `[${field.aliases.map((a) => JSON.stringify(a)).join(', ')}]`;
178
+ const key = /^[a-zA-Z_$][\w$]*$/.test(field.name) ? field.name : JSON.stringify(field.name);
179
+ entries.push(`${key}: { alias: ${alias} }`);
180
+ }
181
+ }
182
+ if (entries.length === 0) return null;
183
+ return `{ ${entries.join(', ')} }`;
184
+ }
@@ -0,0 +1,124 @@
1
+ import { fieldMetaToCode } from '../schema-to-code.ts';
2
+ import type { CommandMeta, GeneratorContext } from '../types.ts';
3
+ import type { CommandFileOptions } from './command-file.ts';
4
+ import { generateCommandFile, toCommandFunctionName } from './command-file.ts';
5
+
6
+ export interface CommandTreeOptions {
7
+ /** When set, generates .wrap() calls instead of .action(). */
8
+ wrap?: {
9
+ /** The external command being wrapped (e.g. 'gh'). */
10
+ command: string;
11
+ };
12
+ }
13
+
14
+ /**
15
+ * Walk a CommandMeta tree and emit one file per command plus a root program file.
16
+ * Maps nested subcommands to a directory structure.
17
+ */
18
+ export function generateCommandTree(root: CommandMeta, ctx: GeneratorContext, options?: CommandTreeOptions): void {
19
+ const rootImports: { name: string; varName: string; path: string; aliases?: string[] }[] = [];
20
+
21
+ // Recursively generate command files (depth-first so children exist before parents)
22
+ function walkCommands(cmd: CommandMeta, dirPath: string, parentArgs: string[]): void {
23
+ if (cmd === root) {
24
+ for (const sub of cmd.subcommands || []) {
25
+ walkCommands(sub, 'commands', []);
26
+ }
27
+ return;
28
+ }
29
+
30
+ const filePath = `${dirPath}/${cmd.name}.ts`;
31
+
32
+ // Recurse into subcommands first so we can reference them
33
+ const childRefs: { name: string; varName: string; importPath: string; aliases?: string[] }[] = [];
34
+ if (cmd.subcommands && cmd.subcommands.length > 0) {
35
+ for (const sub of cmd.subcommands) {
36
+ walkCommands(sub, `${dirPath}/${cmd.name}`, [...parentArgs, cmd.name]);
37
+ childRefs.push({
38
+ name: sub.name,
39
+ varName: toCommandFunctionName(sub.name),
40
+ importPath: `./${cmd.name}/${sub.name}.ts`,
41
+ aliases: sub.aliases,
42
+ });
43
+ }
44
+ }
45
+
46
+ const fileOptions: CommandFileOptions = {};
47
+ if (options?.wrap) {
48
+ fileOptions.wrap = { command: options.wrap.command, args: [...parentArgs, cmd.name] };
49
+ }
50
+ if (childRefs.length > 0) {
51
+ fileOptions.subcommands = childRefs;
52
+ }
53
+
54
+ const code = generateCommandFile(cmd, ctx, Object.keys(fileOptions).length > 0 ? fileOptions : undefined);
55
+ ctx.emitter.addFile(filePath, code.build());
56
+
57
+ rootImports.push({
58
+ name: cmd.name,
59
+ varName: toCommandFunctionName(cmd.name),
60
+ path: `./${filePath.replace(/\.ts$/, '.ts')}`,
61
+ aliases: cmd.aliases,
62
+ });
63
+ }
64
+
65
+ walkCommands(root, '', []);
66
+
67
+ // Generate root program.ts
68
+ const program = ctx.createCodeBuilder();
69
+
70
+ const rootHasArgs = (root.arguments && root.arguments.length > 0) || (root.positionals && root.positionals.length > 0);
71
+ if (rootHasArgs) {
72
+ program.import('z', 'zod/v4');
73
+ }
74
+ program.import(['createPadrone'], 'padrone');
75
+
76
+ // Only import direct children of root
77
+ const directChildren = rootImports.filter((imp) => imp.path.split('/').length <= 3);
78
+ for (const imp of directChildren) {
79
+ program.import([imp.varName], imp.path);
80
+ }
81
+
82
+ program.line();
83
+ program.line(`const program = createPadrone(${JSON.stringify(root.name)})`);
84
+
85
+ // .configure()
86
+ const configParts: string[] = [];
87
+ if (root.description) {
88
+ configParts.push(`description: ${JSON.stringify(root.description)}`);
89
+ }
90
+ if (configParts.length > 0) {
91
+ program.line(` .configure({ ${configParts.join(', ')} })`);
92
+ }
93
+
94
+ // Root arguments (for programs that have options at the root level)
95
+ if (rootHasArgs) {
96
+ const allFields = [...(root.arguments || []), ...(root.positionals || [])];
97
+ const schemaCode = fieldMetaToCode(allFields);
98
+ program.line(` .arguments(${schemaCode.code})`);
99
+ }
100
+
101
+ // Chain .command() calls for direct children
102
+ for (const imp of directChildren) {
103
+ const nameArg =
104
+ imp.aliases && imp.aliases.length > 0
105
+ ? `[${JSON.stringify(imp.name)}, ${imp.aliases.map((a) => JSON.stringify(a)).join(', ')}]`
106
+ : JSON.stringify(imp.name);
107
+ program.line(` .command(${nameArg}, ${imp.varName})`);
108
+ }
109
+
110
+ // If root has no subcommands, add .wrap() or .action()
111
+ if (directChildren.length === 0 && options?.wrap) {
112
+ program.line(` .wrap({ command: ${JSON.stringify(options.wrap.command)} })`);
113
+ }
114
+
115
+ program.line();
116
+ program.line(`export default program`);
117
+
118
+ ctx.emitter.addFile('program.ts', program.build());
119
+
120
+ // Generate index.ts
121
+ const index = ctx.createCodeBuilder();
122
+ index.line(`export { default } from './program.ts'`);
123
+ ctx.emitter.addFile('index.ts', index.build());
124
+ }
@@ -0,0 +1,33 @@
1
+ // Types
2
+
3
+ // Core utilities
4
+ export { createCodeBuilder } from './code-builder.ts';
5
+ export type { DiscoveryOptions, DiscoveryResult, DiscoverySource } from './discovery.ts';
6
+ // Discovery
7
+ export { discoverCli } from './discovery.ts';
8
+ export { createFileEmitter } from './file-emitter.ts';
9
+ // Generators
10
+ export { generateBarrelFile } from './generators/barrel-file.ts';
11
+ export type { CommandFileOptions } from './generators/command-file.ts';
12
+ export { generateCommandFile } from './generators/command-file.ts';
13
+ export type { CommandTreeOptions } from './generators/command-tree.ts';
14
+ export { generateCommandTree } from './generators/command-tree.ts';
15
+ // Parsers
16
+ export { parseFishCompletions } from './parsers/fish.ts';
17
+ export { parseHelpOutput } from './parsers/help.ts';
18
+ export { mergeCommandMeta } from './parsers/merge.ts';
19
+ export { parseZshCompletions } from './parsers/zsh.ts';
20
+ export { fieldMetaToCode, schemaToCode } from './schema-to-code.ts';
21
+ export { template } from './template.ts';
22
+ export type {
23
+ CodeBuilder,
24
+ CodeBuildResult,
25
+ CommandMeta,
26
+ EmitResult,
27
+ FieldMeta,
28
+ FileEmitter,
29
+ FileEmitterOptions,
30
+ GeneratorContext,
31
+ GeneratorLogger,
32
+ TemplateFunction,
33
+ } from './types.ts';
@@ -0,0 +1,163 @@
1
+ import type { CommandMeta, FieldMeta } from '../types.ts';
2
+
3
+ /**
4
+ * Parse fish shell completion scripts into CommandMeta.
5
+ *
6
+ * Fish completions use the `complete` builtin:
7
+ * complete -c <command> -s <short> -l <long> -d <description> -a <arguments> -r -f
8
+ */
9
+ export function parseFishCompletions(text: string): CommandMeta {
10
+ const lines = text.split('\n');
11
+ const result: CommandMeta = {
12
+ name: '',
13
+ arguments: [],
14
+ subcommands: [],
15
+ };
16
+
17
+ const subcommandMap = new Map<string, CommandMeta>();
18
+
19
+ for (const line of lines) {
20
+ const trimmed = line.trim();
21
+ if (!trimmed || trimmed.startsWith('#')) continue;
22
+
23
+ // Match: complete -c <command> [options]
24
+ const completeMatch = trimmed.match(/^complete\s+/);
25
+ if (!completeMatch) continue;
26
+
27
+ const parts = parseCompleteLine(trimmed);
28
+ if (!parts) continue;
29
+
30
+ // Set root command name
31
+ if (!result.name && parts.command) {
32
+ result.name = parts.command;
33
+ }
34
+
35
+ // If this completion has a condition like "__fish_seen_subcommand_from <sub>"
36
+ // it belongs to a subcommand
37
+ const subcommandCondition = parts.condition?.match(/__fish_seen_subcommand_from\s+(\S+)/);
38
+ if (subcommandCondition) {
39
+ const subName = subcommandCondition[1]!;
40
+ let sub = subcommandMap.get(subName);
41
+ if (!sub) {
42
+ sub = { name: subName, arguments: [] };
43
+ subcommandMap.set(subName, sub);
44
+ }
45
+
46
+ if (parts.longFlag || parts.shortFlag) {
47
+ const field = completionToField(parts);
48
+ if (field) sub.arguments!.push(field);
49
+ }
50
+ continue;
51
+ }
52
+
53
+ // If this defines a subcommand (has -a with no flags)
54
+ if (parts.arguments && !parts.longFlag && !parts.shortFlag) {
55
+ // Arguments list could be subcommand names
56
+ const names = parts.arguments.split(/\s+/);
57
+ for (const name of names) {
58
+ if (!name || name.startsWith('(')) continue;
59
+ if (!subcommandMap.has(name)) {
60
+ subcommandMap.set(name, {
61
+ name,
62
+ description: parts.description,
63
+ arguments: [],
64
+ });
65
+ } else if (parts.description) {
66
+ subcommandMap.get(name)!.description = parts.description;
67
+ }
68
+ }
69
+ continue;
70
+ }
71
+
72
+ // Global option
73
+ if (parts.longFlag || parts.shortFlag) {
74
+ const field = completionToField(parts);
75
+ if (field) result.arguments!.push(field);
76
+ }
77
+ }
78
+
79
+ // Add subcommands
80
+ for (const sub of subcommandMap.values()) {
81
+ if (sub.arguments!.length === 0) delete sub.arguments;
82
+ result.subcommands!.push(sub);
83
+ }
84
+
85
+ if (result.arguments!.length === 0) delete result.arguments;
86
+ if (result.subcommands!.length === 0) delete result.subcommands;
87
+
88
+ return result;
89
+ }
90
+
91
+ interface CompleteParts {
92
+ command?: string;
93
+ shortFlag?: string;
94
+ longFlag?: string;
95
+ description?: string;
96
+ arguments?: string;
97
+ condition?: string;
98
+ requiresArg?: boolean;
99
+ noFiles?: boolean;
100
+ }
101
+
102
+ function parseCompleteLine(line: string): CompleteParts | null {
103
+ const parts: CompleteParts = {};
104
+
105
+ // Extract -c <command>
106
+ const cmdMatch = line.match(/-c\s+(\S+)/);
107
+ if (cmdMatch) parts.command = cmdMatch[1];
108
+
109
+ // Extract -s <short>
110
+ const shortMatch = line.match(/-s\s+(\S+)/);
111
+ if (shortMatch) parts.shortFlag = shortMatch[1];
112
+
113
+ // Extract -l <long>
114
+ const longMatch = line.match(/-l\s+(\S+)/);
115
+ if (longMatch) parts.longFlag = longMatch[1];
116
+
117
+ // Extract -d '<description>' or -d "<description>"
118
+ const descMatch = line.match(/-d\s+['"]([^'"]+)['"]/) || line.match(/-d\s+(\S+)/);
119
+ if (descMatch) parts.description = descMatch[1];
120
+
121
+ // Extract -a '<arguments>'
122
+ const argsMatch = line.match(/-a\s+['"]([^'"]+)['"]/) || line.match(/-a\s+(\S+)/);
123
+ if (argsMatch) parts.arguments = argsMatch[1];
124
+
125
+ // Extract -n '<condition>'
126
+ const condMatch = line.match(/-n\s+['"]([^'"]+)['"]/) || line.match(/-n\s+(\S+)/);
127
+ if (condMatch) parts.condition = condMatch[1];
128
+
129
+ // -r means requires argument
130
+ parts.requiresArg = /-r\b/.test(line);
131
+ // -f means no file completion
132
+ parts.noFiles = /-f\b/.test(line);
133
+
134
+ return parts;
135
+ }
136
+
137
+ function completionToField(parts: CompleteParts): FieldMeta | null {
138
+ const name = parts.longFlag ? parts.longFlag.replace(/-([a-z])/g, (_, c: string) => c.toUpperCase()) : parts.shortFlag || '';
139
+
140
+ if (!name) return null;
141
+
142
+ let type: FieldMeta['type'] = parts.requiresArg ? 'string' : 'boolean';
143
+ let enumValues: string[] | undefined;
144
+
145
+ // If -a provides specific values, treat as enum
146
+ if (parts.arguments) {
147
+ const values = parts.arguments.split(/\s+/).filter((v) => !v.startsWith('('));
148
+ if (values.length > 0 && values.length <= 20) {
149
+ enumValues = values;
150
+ type = 'enum';
151
+ }
152
+ }
153
+
154
+ const aliases = parts.shortFlag && parts.longFlag ? [`-${parts.shortFlag}`] : undefined;
155
+
156
+ return {
157
+ name,
158
+ type,
159
+ description: parts.description,
160
+ aliases,
161
+ enumValues,
162
+ };
163
+ }