padrone 1.3.0 → 1.5.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/CHANGELOG.md +94 -0
- package/README.md +105 -284
- package/dist/{args-DFEI7_G_.mjs → args-D5PNDyNu.mjs} +46 -21
- package/dist/args-D5PNDyNu.mjs.map +1 -0
- package/dist/chunk-CjcI7cDX.mjs +15 -0
- package/dist/codegen/index.d.mts +28 -3
- package/dist/codegen/index.d.mts.map +1 -1
- package/dist/codegen/index.mjs +169 -19
- package/dist/codegen/index.mjs.map +1 -1
- package/dist/command-utils-B1D-HqCd.mjs +1117 -0
- package/dist/command-utils-B1D-HqCd.mjs.map +1 -0
- package/dist/completion.d.mts +1 -1
- package/dist/completion.d.mts.map +1 -1
- package/dist/completion.mjs +77 -29
- package/dist/completion.mjs.map +1 -1
- package/dist/docs/index.d.mts +22 -2
- package/dist/docs/index.d.mts.map +1 -1
- package/dist/docs/index.mjs +94 -7
- package/dist/docs/index.mjs.map +1 -1
- package/dist/errors-BiVrBgi6.mjs +114 -0
- package/dist/errors-BiVrBgi6.mjs.map +1 -0
- package/dist/{formatter-XroimS3Q.d.mts → formatter-DtHzbP22.d.mts} +35 -5
- package/dist/formatter-DtHzbP22.d.mts.map +1 -0
- package/dist/help-bbmu9-qd.mjs +735 -0
- package/dist/help-bbmu9-qd.mjs.map +1 -0
- package/dist/index.d.mts +32 -3
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +495 -267
- package/dist/index.mjs.map +1 -1
- package/dist/mcp-mLWIdUIu.mjs +379 -0
- package/dist/mcp-mLWIdUIu.mjs.map +1 -0
- package/dist/serve-B0u43DK7.mjs +404 -0
- package/dist/serve-B0u43DK7.mjs.map +1 -0
- package/dist/stream-BcC146Ud.mjs +56 -0
- package/dist/stream-BcC146Ud.mjs.map +1 -0
- package/dist/test.d.mts +1 -1
- package/dist/test.mjs +4 -15
- package/dist/test.mjs.map +1 -1
- package/dist/{types-BS7RP5Ls.d.mts → types-Ch8Mk6Qb.d.mts} +311 -63
- package/dist/types-Ch8Mk6Qb.d.mts.map +1 -0
- package/dist/{update-check-EbNDkzyV.mjs → update-check-CFX1FV3v.mjs} +2 -2
- package/dist/{update-check-EbNDkzyV.mjs.map → update-check-CFX1FV3v.mjs.map} +1 -1
- package/dist/zod.d.mts +32 -0
- package/dist/zod.d.mts.map +1 -0
- package/dist/zod.mjs +50 -0
- package/dist/zod.mjs.map +1 -0
- package/package.json +10 -2
- package/src/args.ts +76 -44
- package/src/cli/docs.ts +1 -7
- package/src/cli/doctor.ts +195 -10
- package/src/cli/index.ts +1 -1
- package/src/cli/init.ts +2 -3
- package/src/cli/link.ts +2 -2
- package/src/codegen/discovery.ts +80 -28
- package/src/codegen/index.ts +2 -1
- package/src/codegen/parsers/bash.ts +179 -0
- package/src/codegen/schema-to-code.ts +2 -1
- package/src/colorizer.ts +126 -13
- package/src/command-utils.ts +401 -23
- package/src/completion.ts +120 -47
- package/src/create.ts +483 -130
- package/src/docs/index.ts +122 -8
- package/src/formatter.ts +173 -125
- package/src/help.ts +46 -12
- package/src/index.ts +29 -1
- package/src/interactive.ts +45 -4
- package/src/mcp.ts +390 -0
- package/src/repl-loop.ts +16 -3
- package/src/runtime.ts +195 -2
- package/src/serve.ts +442 -0
- package/src/stream.ts +75 -0
- package/src/test.ts +7 -16
- package/src/type-utils.ts +28 -4
- package/src/types.ts +212 -30
- package/src/wrap.ts +23 -25
- package/src/zod.ts +50 -0
- package/dist/args-DFEI7_G_.mjs.map +0 -1
- package/dist/chunk-y_GBKt04.mjs +0 -5
- package/dist/formatter-XroimS3Q.d.mts.map +0 -1
- package/dist/help-CgGP7hQU.mjs +0 -1229
- package/dist/help-CgGP7hQU.mjs.map +0 -1
- package/dist/types-BS7RP5Ls.d.mts.map +0 -1
package/src/formatter.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { camelToKebab } from './args.ts';
|
|
2
|
-
import { createColorizer } from './colorizer.ts';
|
|
2
|
+
import { type ColorConfig, type ColorTheme, createColorizer } from './colorizer.ts';
|
|
3
3
|
|
|
4
4
|
export type HelpFormat = 'text' | 'ansi' | 'console' | 'markdown' | 'html' | 'json';
|
|
5
5
|
export type HelpDetail = 'minimal' | 'standard' | 'full';
|
|
@@ -17,6 +17,7 @@ export type HelpPositionalInfo = {
|
|
|
17
17
|
optional: boolean;
|
|
18
18
|
default?: unknown;
|
|
19
19
|
type?: string;
|
|
20
|
+
enum?: string[];
|
|
20
21
|
};
|
|
21
22
|
|
|
22
23
|
/**
|
|
@@ -44,6 +45,8 @@ export type HelpArgumentInfo = {
|
|
|
44
45
|
negatable?: boolean;
|
|
45
46
|
/** Config file key that maps to this arg */
|
|
46
47
|
configKey?: string;
|
|
48
|
+
/** Group name for organizing this option under a labeled section in help output */
|
|
49
|
+
group?: string;
|
|
47
50
|
};
|
|
48
51
|
|
|
49
52
|
/**
|
|
@@ -57,6 +60,8 @@ export type HelpSubcommandInfo = {
|
|
|
57
60
|
deprecated?: boolean | string;
|
|
58
61
|
hidden?: boolean;
|
|
59
62
|
hasSubcommands?: boolean;
|
|
63
|
+
/** Group name for organizing this command under a labeled section in help output */
|
|
64
|
+
group?: string;
|
|
60
65
|
};
|
|
61
66
|
|
|
62
67
|
/**
|
|
@@ -102,6 +107,8 @@ export type HelpInfo = {
|
|
|
102
107
|
arguments?: HelpArgumentInfo[];
|
|
103
108
|
/** Built-in commands and flags (shown only for root command) */
|
|
104
109
|
builtins?: HelpBuiltinInfo[];
|
|
110
|
+
/** Command-level usage examples (shown in help output) */
|
|
111
|
+
examples?: string[];
|
|
105
112
|
/** Full help info for nested commands (used in 'full' detail mode) */
|
|
106
113
|
nestedCommands?: HelpInfo[];
|
|
107
114
|
};
|
|
@@ -132,6 +139,7 @@ type Styler = {
|
|
|
132
139
|
type: (text: string) => string;
|
|
133
140
|
description: (text: string) => string;
|
|
134
141
|
label: (text: string) => string;
|
|
142
|
+
section: (text: string) => string;
|
|
135
143
|
meta: (text: string) => string;
|
|
136
144
|
example: (text: string) => string;
|
|
137
145
|
exampleValue: (text: string) => string;
|
|
@@ -146,7 +154,6 @@ type LayoutConfig = {
|
|
|
146
154
|
indent: (level: number) => string;
|
|
147
155
|
join: (parts: string[]) => string;
|
|
148
156
|
wrapDocument?: (content: string) => string;
|
|
149
|
-
usageLabel: string;
|
|
150
157
|
};
|
|
151
158
|
|
|
152
159
|
// ============================================================================
|
|
@@ -160,6 +167,7 @@ function createTextStyler(): Styler {
|
|
|
160
167
|
type: (text) => text,
|
|
161
168
|
description: (text) => text,
|
|
162
169
|
label: (text) => text,
|
|
170
|
+
section: (text) => text,
|
|
163
171
|
meta: (text) => text,
|
|
164
172
|
example: (text) => text,
|
|
165
173
|
exampleValue: (text) => text,
|
|
@@ -167,14 +175,15 @@ function createTextStyler(): Styler {
|
|
|
167
175
|
};
|
|
168
176
|
}
|
|
169
177
|
|
|
170
|
-
function createAnsiStyler(): Styler {
|
|
171
|
-
const colorizer = createColorizer();
|
|
178
|
+
function createAnsiStyler(theme?: ColorTheme | ColorConfig): Styler {
|
|
179
|
+
const colorizer = createColorizer(theme);
|
|
172
180
|
return {
|
|
173
181
|
command: colorizer.command,
|
|
174
182
|
arg: colorizer.arg,
|
|
175
183
|
type: colorizer.type,
|
|
176
184
|
description: colorizer.description,
|
|
177
185
|
label: colorizer.label,
|
|
186
|
+
section: colorizer.label,
|
|
178
187
|
meta: colorizer.meta,
|
|
179
188
|
example: colorizer.example,
|
|
180
189
|
exampleValue: colorizer.exampleValue,
|
|
@@ -182,30 +191,8 @@ function createAnsiStyler(): Styler {
|
|
|
182
191
|
};
|
|
183
192
|
}
|
|
184
193
|
|
|
185
|
-
function createConsoleStyler(): Styler {
|
|
186
|
-
|
|
187
|
-
reset: '\x1b[0m',
|
|
188
|
-
bold: '\x1b[1m',
|
|
189
|
-
dim: '\x1b[2m',
|
|
190
|
-
italic: '\x1b[3m',
|
|
191
|
-
underline: '\x1b[4m',
|
|
192
|
-
strikethrough: '\x1b[9m',
|
|
193
|
-
cyan: '\x1b[36m',
|
|
194
|
-
green: '\x1b[32m',
|
|
195
|
-
yellow: '\x1b[33m',
|
|
196
|
-
gray: '\x1b[90m',
|
|
197
|
-
};
|
|
198
|
-
return {
|
|
199
|
-
command: (text) => `${colors.cyan}${colors.bold}${text}${colors.reset}`,
|
|
200
|
-
arg: (text) => `${colors.green}${text}${colors.reset}`,
|
|
201
|
-
type: (text) => `${colors.yellow}${text}${colors.reset}`,
|
|
202
|
-
description: (text) => `${colors.dim}${text}${colors.reset}`,
|
|
203
|
-
label: (text) => `${colors.bold}${text}${colors.reset}`,
|
|
204
|
-
meta: (text) => `${colors.gray}${text}${colors.reset}`,
|
|
205
|
-
example: (text) => `${colors.underline}${text}${colors.reset}`,
|
|
206
|
-
exampleValue: (text) => `${colors.italic}${text}${colors.reset}`,
|
|
207
|
-
deprecated: (text) => `${colors.strikethrough}${colors.gray}${text}${colors.reset}`,
|
|
208
|
-
};
|
|
194
|
+
function createConsoleStyler(theme?: ColorTheme | ColorConfig): Styler {
|
|
195
|
+
return createAnsiStyler(theme);
|
|
209
196
|
}
|
|
210
197
|
|
|
211
198
|
function createMarkdownStyler(): Styler {
|
|
@@ -214,7 +201,8 @@ function createMarkdownStyler(): Styler {
|
|
|
214
201
|
arg: (text) => `\`${text}\``,
|
|
215
202
|
type: (text) => `\`${text}\``,
|
|
216
203
|
description: (text) => text,
|
|
217
|
-
label: (text) =>
|
|
204
|
+
label: (text) => `**${text}**`,
|
|
205
|
+
section: (text) => `### ${text}`,
|
|
218
206
|
meta: (text) => `*${text}*`,
|
|
219
207
|
example: (text) => `**${text}**`,
|
|
220
208
|
exampleValue: (text) => `\`${text}\``,
|
|
@@ -232,7 +220,8 @@ function createHtmlStyler(): Styler {
|
|
|
232
220
|
arg: (text) => `<code style="color: #4caf50;">${escapeHtml(text)}</code>`,
|
|
233
221
|
type: (text) => `<code style="color: #ff9800;">${escapeHtml(text)}</code>`,
|
|
234
222
|
description: (text) => `<span style="color: #666;">${escapeHtml(text)}</span>`,
|
|
235
|
-
label: (text) => `<
|
|
223
|
+
label: (text) => `<strong>${escapeHtml(text)}</strong>`,
|
|
224
|
+
section: (text) => `<h3>${escapeHtml(text)}</h3>`,
|
|
236
225
|
meta: (text) => `<span style="color: #999;">${escapeHtml(text)}</span>`,
|
|
237
226
|
example: (text) => `<strong style="text-decoration: underline;">${escapeHtml(text)}</strong>`,
|
|
238
227
|
exampleValue: (text) => `<em>${escapeHtml(text)}</em>`,
|
|
@@ -249,7 +238,6 @@ function createTextLayout(): LayoutConfig {
|
|
|
249
238
|
newline: '\n',
|
|
250
239
|
indent: (level) => ' '.repeat(level),
|
|
251
240
|
join: (parts) => parts.filter(Boolean).join(' '),
|
|
252
|
-
usageLabel: 'Usage:',
|
|
253
241
|
};
|
|
254
242
|
}
|
|
255
243
|
|
|
@@ -262,7 +250,6 @@ function createMarkdownLayout(): LayoutConfig {
|
|
|
262
250
|
return ' ';
|
|
263
251
|
},
|
|
264
252
|
join: (parts) => parts.filter(Boolean).join(' '),
|
|
265
|
-
usageLabel: 'Usage:',
|
|
266
253
|
};
|
|
267
254
|
}
|
|
268
255
|
|
|
@@ -272,7 +259,6 @@ function createHtmlLayout(): LayoutConfig {
|
|
|
272
259
|
indent: (level) => ' '.repeat(level),
|
|
273
260
|
join: (parts) => parts.filter(Boolean).join(' '),
|
|
274
261
|
wrapDocument: (content) => `<div style="font-family: monospace; line-height: 1.6;">${content}</div>`,
|
|
275
|
-
usageLabel: '<strong>Usage:</strong>',
|
|
276
262
|
};
|
|
277
263
|
}
|
|
278
264
|
|
|
@@ -283,8 +269,8 @@ function createHtmlLayout(): LayoutConfig {
|
|
|
283
269
|
/**
|
|
284
270
|
* Creates a formatter that uses the given styler and layout configuration.
|
|
285
271
|
*/
|
|
286
|
-
function createGenericFormatter(styler: Styler, layout: LayoutConfig): Formatter {
|
|
287
|
-
const { newline, indent, join, wrapDocument
|
|
272
|
+
function createGenericFormatter(styler: Styler, layout: LayoutConfig, showAllBuiltins?: boolean): Formatter {
|
|
273
|
+
const { newline, indent, join, wrapDocument } = layout;
|
|
288
274
|
|
|
289
275
|
function formatUsageSection(info: HelpInfo): string[] {
|
|
290
276
|
const usageParts: string[] = [styler.command(info.usage.command), info.usage.hasSubcommands ? styler.meta('[command]') : ''];
|
|
@@ -296,15 +282,13 @@ function createGenericFormatter(styler: Styler, layout: LayoutConfig): Formatter
|
|
|
296
282
|
}
|
|
297
283
|
}
|
|
298
284
|
if (info.usage.hasArguments) usageParts.push(styler.meta('[options]'));
|
|
299
|
-
return [`${
|
|
285
|
+
return [`${styler.label('Usage:')} ${join(usageParts)}`];
|
|
300
286
|
}
|
|
301
287
|
|
|
302
288
|
function formatSubcommandsSection(info: HelpInfo): string[] {
|
|
303
289
|
const lines: string[] = [];
|
|
304
290
|
const subcommands = info.subcommands!;
|
|
305
291
|
|
|
306
|
-
lines.push(styler.label('Commands:'));
|
|
307
|
-
|
|
308
292
|
const subcommandSuffix = (c: HelpSubcommandInfo) => (c.hasSubcommands ? ' <subcommand>' : '');
|
|
309
293
|
const formatAliasParts = (c: HelpSubcommandInfo) => {
|
|
310
294
|
if (!c.aliases?.length) return { plain: '', styled: '' };
|
|
@@ -322,40 +306,55 @@ function createGenericFormatter(styler: Styler, layout: LayoutConfig): Formatter
|
|
|
322
306
|
}
|
|
323
307
|
return { plain: parts.length ? ` ${parts.join(' ')}` : '', styled: styledParts.length ? ` ${styledParts.join(' ')}` : '' };
|
|
324
308
|
};
|
|
309
|
+
// Column width is computed across all subcommands for consistent alignment
|
|
325
310
|
const maxNameLength = Math.max(
|
|
326
311
|
...subcommands.map((c) => {
|
|
327
312
|
return (c.name + subcommandSuffix(c) + formatAliasParts(c).plain).length;
|
|
328
313
|
}),
|
|
329
314
|
);
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
315
|
+
|
|
316
|
+
const grouped = Object.groupBy(subcommands, (c) => c.group ?? '');
|
|
317
|
+
|
|
318
|
+
const renderSubcommands = (cmds: HelpSubcommandInfo[]) => {
|
|
319
|
+
for (const subCmd of cmds) {
|
|
320
|
+
const aliasParts = formatAliasParts(subCmd);
|
|
321
|
+
const suffix = subcommandSuffix(subCmd);
|
|
322
|
+
const commandDisplay = subCmd.name + suffix + aliasParts.plain;
|
|
323
|
+
const padding = ' '.repeat(Math.max(0, maxNameLength - commandDisplay.length + 2));
|
|
324
|
+
const isDeprecated = !!subCmd.deprecated;
|
|
325
|
+
const isDefaultEntry = subCmd.name === '[default]';
|
|
326
|
+
const commandName = isDeprecated
|
|
327
|
+
? styler.deprecated(commandDisplay)
|
|
328
|
+
: (isDefaultEntry ? styler.meta(subCmd.name) : styler.command(subCmd.name)) +
|
|
329
|
+
(suffix ? styler.meta(suffix) : '') +
|
|
330
|
+
aliasParts.styled;
|
|
331
|
+
const lineParts: string[] = [commandName, padding];
|
|
332
|
+
|
|
333
|
+
// Use title if available, otherwise use description
|
|
334
|
+
const displayText = subCmd.title ?? subCmd.description;
|
|
335
|
+
if (displayText) {
|
|
336
|
+
lineParts.push(isDeprecated ? styler.deprecated(displayText) : styler.description(displayText));
|
|
337
|
+
}
|
|
338
|
+
if (isDeprecated) {
|
|
339
|
+
const deprecatedMeta =
|
|
340
|
+
typeof subCmd.deprecated === 'string' ? styler.meta(` (deprecated: ${subCmd.deprecated})`) : styler.meta(' (deprecated)');
|
|
341
|
+
lineParts.push(deprecatedMeta);
|
|
342
|
+
}
|
|
343
|
+
lines.push(indent(1) + lineParts.join(''));
|
|
353
344
|
}
|
|
354
|
-
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
for (const [key, items] of Object.entries(grouped)) {
|
|
348
|
+
if (lines.length > 0) lines.push('');
|
|
349
|
+
lines.push(styler.section(key ? `${key}:` : 'Commands:'));
|
|
350
|
+
renderSubcommands(items!);
|
|
355
351
|
}
|
|
356
352
|
|
|
357
|
-
|
|
358
|
-
|
|
353
|
+
// Skip hint when builtins are present — the builtins section shows a combined hint
|
|
354
|
+
if (!info.builtins?.length) {
|
|
355
|
+
lines.push('');
|
|
356
|
+
lines.push(styler.meta(`Run "${info.name} [command] --help" for more information on a command.`));
|
|
357
|
+
}
|
|
359
358
|
|
|
360
359
|
return lines;
|
|
361
360
|
}
|
|
@@ -364,7 +363,7 @@ function createGenericFormatter(styler: Styler, layout: LayoutConfig): Formatter
|
|
|
364
363
|
const lines: string[] = [];
|
|
365
364
|
const args = info.positionals!;
|
|
366
365
|
|
|
367
|
-
lines.push(styler.
|
|
366
|
+
lines.push(styler.section('Arguments:'));
|
|
368
367
|
|
|
369
368
|
const maxNameLength = Math.min(32, Math.max(...args.map((a) => a.name.length)));
|
|
370
369
|
|
|
@@ -373,6 +372,7 @@ function createGenericFormatter(styler: Styler, layout: LayoutConfig): Formatter
|
|
|
373
372
|
const descParts: string[] = [];
|
|
374
373
|
if (arg.description) descParts.push(styler.description(arg.description));
|
|
375
374
|
if (info.usage.stdinField === arg.name) descParts.push(styler.meta('(stdin)'));
|
|
375
|
+
if (arg.enum) descParts.push(styler.meta(`(choices: ${arg.enum.join(', ')})`));
|
|
376
376
|
if (arg.default !== undefined) descParts.push(styler.meta(`(default: ${String(arg.default)})`));
|
|
377
377
|
lines.push(indent(1) + styler.arg(arg.name) + padding + join(descParts));
|
|
378
378
|
}
|
|
@@ -384,8 +384,6 @@ function createGenericFormatter(styler: Styler, layout: LayoutConfig): Formatter
|
|
|
384
384
|
const lines: string[] = [];
|
|
385
385
|
const argList = info.arguments || [];
|
|
386
386
|
|
|
387
|
-
lines.push(styler.label('Options:'));
|
|
388
|
-
|
|
389
387
|
// Helper to check if a default value is meaningful (not empty string/array)
|
|
390
388
|
const hasDefault = (value: unknown): boolean => {
|
|
391
389
|
if (value === undefined) return false;
|
|
@@ -394,70 +392,94 @@ function createGenericFormatter(styler: Styler, layout: LayoutConfig): Formatter
|
|
|
394
392
|
return true;
|
|
395
393
|
};
|
|
396
394
|
|
|
397
|
-
// Build
|
|
398
|
-
const argColumns
|
|
395
|
+
// Build columns: flags | names | type | description
|
|
396
|
+
const argColumns = argList.map((arg) => {
|
|
399
397
|
// Promote kebab-case alias to primary display name if it exists
|
|
400
398
|
const kebab = camelToKebab(arg.name);
|
|
401
399
|
const primaryName = kebab && arg.aliases?.includes(kebab) ? kebab : arg.name;
|
|
402
400
|
const remainingAliases = arg.aliases?.filter((a) => a !== primaryName);
|
|
403
|
-
const argName = `--${primaryName}`;
|
|
404
|
-
const flagNames = arg.flags?.length ? arg.flags.map((f) => `-${f}`).join(', ') : '';
|
|
405
|
-
const aliasNames = remainingAliases?.length ? remainingAliases.map((a) => `--${a}`).join(', ') : '';
|
|
406
|
-
const shortNames = [flagNames, aliasNames].filter(Boolean).join(', ');
|
|
407
|
-
const fullArgName = shortNames ? `${argName}, ${shortNames}` : argName;
|
|
408
|
-
const isDeprecated = !!arg.deprecated;
|
|
409
|
-
const formattedArgName = isDeprecated ? styler.deprecated(fullArgName) : styler.arg(fullArgName);
|
|
410
401
|
|
|
411
|
-
const
|
|
412
|
-
const
|
|
402
|
+
const flagsPlain = arg.flags?.length ? arg.flags.map((f) => `-${f}`).join(', ') : '';
|
|
403
|
+
const namesPlain = [`--${primaryName}`, ...(remainingAliases?.map((a) => `--${a}`) || [])].join(', ');
|
|
404
|
+
const typePlain = arg.type && arg.type !== 'boolean' ? (arg.optional ? `[${arg.type}]` : `<${arg.type}>`) : '';
|
|
413
405
|
|
|
414
|
-
|
|
415
|
-
const typePart = arg.optional ? `[${arg.type}]` : `<${arg.type}>`;
|
|
416
|
-
plainParts.push(typePart);
|
|
417
|
-
styledParts.push(styler.type(typePart));
|
|
418
|
-
}
|
|
419
|
-
if (isDeprecated) {
|
|
420
|
-
const deprecatedPart = typeof arg.deprecated === 'string' ? `(deprecated: ${arg.deprecated})` : '(deprecated)';
|
|
421
|
-
plainParts.push(deprecatedPart);
|
|
422
|
-
styledParts.push(styler.meta(deprecatedPart));
|
|
423
|
-
}
|
|
406
|
+
const isDeprecated = !!arg.deprecated;
|
|
424
407
|
|
|
425
|
-
return {
|
|
408
|
+
return { flagsPlain, namesPlain, typePlain, isDeprecated, arg };
|
|
426
409
|
});
|
|
427
410
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
411
|
+
// Column widths are computed across all args (regardless of group) for consistent alignment
|
|
412
|
+
const maxFlagsWidth = Math.min(12, Math.max(0, ...argColumns.map((c) => c.flagsPlain.length)));
|
|
413
|
+
const maxNamesWidth = Math.min(32, Math.max(0, ...argColumns.map((c) => c.namesPlain.length)));
|
|
414
|
+
const maxTypeWidth = Math.min(16, Math.max(0, ...argColumns.map((c) => c.typePlain.length)));
|
|
415
|
+
const hasAnyFlags = maxFlagsWidth > 0;
|
|
416
|
+
|
|
417
|
+
// Split into ordered groups: ungrouped first as "Options:", then each group in first-seen order
|
|
418
|
+
const grouped = Object.groupBy(argColumns, (c) => c.arg.group ?? '');
|
|
419
|
+
|
|
420
|
+
const renderArgColumns = (columns: typeof argColumns) => {
|
|
421
|
+
for (const { flagsPlain, namesPlain, typePlain, isDeprecated, arg } of columns) {
|
|
422
|
+
const parts: string[] = [];
|
|
423
|
+
|
|
424
|
+
// Flags column
|
|
425
|
+
if (hasAnyFlags) {
|
|
426
|
+
const styledFlags = isDeprecated ? (flagsPlain ? styler.deprecated(flagsPlain) : '') : flagsPlain ? styler.arg(flagsPlain) : '';
|
|
427
|
+
const flagsPadding = ' '.repeat(Math.max(0, maxFlagsWidth - flagsPlain.length));
|
|
428
|
+
const separator = flagsPlain ? ', ' : ' ';
|
|
429
|
+
parts.push(styledFlags + flagsPadding + separator);
|
|
430
|
+
}
|
|
439
431
|
|
|
440
|
-
|
|
432
|
+
// Names column
|
|
433
|
+
const styledNames = isDeprecated ? styler.deprecated(namesPlain) : styler.arg(namesPlain);
|
|
434
|
+
const namesPadding = ' '.repeat(Math.max(2, maxNamesWidth - namesPlain.length + 2));
|
|
435
|
+
parts.push(styledNames + namesPadding);
|
|
441
436
|
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
437
|
+
// Type column
|
|
438
|
+
if (maxTypeWidth > 0) {
|
|
439
|
+
const styledType = typePlain ? styler.type(typePlain) : '';
|
|
440
|
+
const typePadding = ' '.repeat(Math.max(2, maxTypeWidth - typePlain.length + 2));
|
|
441
|
+
parts.push(styledType + typePadding);
|
|
442
|
+
}
|
|
448
443
|
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
lines.push(indent(
|
|
444
|
+
// Line 1: description
|
|
445
|
+
const descParts: string[] = [];
|
|
446
|
+
if (arg.description) descParts.push(isDeprecated ? styler.deprecated(arg.description) : styler.description(arg.description));
|
|
447
|
+
lines.push(indent(1) + parts.join('') + join(descParts));
|
|
448
|
+
|
|
449
|
+
// Line 2: deprecated (no reason), default, choices
|
|
450
|
+
const line2Parts: string[] = [];
|
|
451
|
+
if (isDeprecated && typeof arg.deprecated !== 'string') line2Parts.push(styler.meta('(deprecated)'));
|
|
452
|
+
if (hasDefault(arg.default)) line2Parts.push(styler.meta(`(default: ${String(arg.default)})`));
|
|
453
|
+
if (arg.enum) line2Parts.push(styler.meta(`(choices: ${arg.enum.join(', ')})`));
|
|
454
|
+
if (line2Parts.length > 0) lines.push(indent(3) + join(line2Parts));
|
|
455
|
+
|
|
456
|
+
// Line 3: deprecated (with reason), examples
|
|
457
|
+
const line3Parts: string[] = [];
|
|
458
|
+
if (isDeprecated && typeof arg.deprecated === 'string') line3Parts.push(styler.meta(`(deprecated: ${arg.deprecated})`));
|
|
459
|
+
if (arg.examples && arg.examples.length > 0) {
|
|
460
|
+
const exampleValues = arg.examples.map((example) => (typeof example === 'string' ? example : JSON.stringify(example))).join(', ');
|
|
461
|
+
line3Parts.push(styler.example('Example:'), styler.exampleValue(exampleValues));
|
|
462
|
+
}
|
|
463
|
+
if (line3Parts.length > 0) lines.push(indent(3) + join(line3Parts));
|
|
464
|
+
|
|
465
|
+
// Line 4: stdin, env, config
|
|
466
|
+
const line4Parts: string[] = [];
|
|
467
|
+
if (info.usage.stdinField === arg.name) line4Parts.push(styler.meta('(stdin)'));
|
|
468
|
+
if (arg.env) {
|
|
469
|
+
const envVars = typeof arg.env === 'string' ? [arg.env] : arg.env;
|
|
470
|
+
line4Parts.push(styler.example('Env:'), styler.exampleValue(envVars.join(', ')));
|
|
471
|
+
}
|
|
472
|
+
if (arg.configKey) {
|
|
473
|
+
line4Parts.push(styler.example('Config:'), styler.exampleValue(arg.configKey));
|
|
474
|
+
}
|
|
475
|
+
if (line4Parts.length > 0) lines.push(indent(3) + join(line4Parts));
|
|
453
476
|
}
|
|
477
|
+
};
|
|
454
478
|
|
|
455
|
-
|
|
456
|
-
if (
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
lines.push(indent(3) + join(exampleParts));
|
|
460
|
-
}
|
|
479
|
+
for (const [key, items] of Object.entries(grouped)) {
|
|
480
|
+
if (lines.length > 0) lines.push('');
|
|
481
|
+
lines.push(styler.section(key ? `${key}:` : 'Options:'));
|
|
482
|
+
renderArgColumns(items!);
|
|
461
483
|
}
|
|
462
484
|
|
|
463
485
|
return lines;
|
|
@@ -467,7 +489,18 @@ function createGenericFormatter(styler: Styler, layout: LayoutConfig): Formatter
|
|
|
467
489
|
const lines: string[] = [];
|
|
468
490
|
const builtins = info.builtins!;
|
|
469
491
|
|
|
470
|
-
|
|
492
|
+
if (!showAllBuiltins) {
|
|
493
|
+
// Collapsed summary: show selected builtins on a single line with a combined hint
|
|
494
|
+
const highlights = ['help [command]', 'version', '[command] --repl'];
|
|
495
|
+
lines.push(`${styler.label('Global:')} ${styler.meta(highlights.join(', '))}`);
|
|
496
|
+
const hint = info.usage.hasSubcommands
|
|
497
|
+
? `Run "${info.name} [command] --help" for more information. Use "--all" for all global commands.`
|
|
498
|
+
: `Use "${info.name} help --all" for more information on global commands.`;
|
|
499
|
+
lines.push(styler.meta(hint));
|
|
500
|
+
return lines;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
lines.push(styler.section('Global:'));
|
|
471
504
|
|
|
472
505
|
// Compute max effective name length for alignment across main and sub entries
|
|
473
506
|
const allLengths: number[] = [];
|
|
@@ -535,6 +568,15 @@ function createGenericFormatter(styler: Styler, layout: LayoutConfig): Formatter
|
|
|
535
568
|
lines.push('');
|
|
536
569
|
}
|
|
537
570
|
|
|
571
|
+
// Examples section (if present)
|
|
572
|
+
if (info.examples && info.examples.length > 0) {
|
|
573
|
+
lines.push(styler.section('Examples:'));
|
|
574
|
+
for (const ex of info.examples) {
|
|
575
|
+
lines.push(indent(1) + styler.meta('$ ') + styler.exampleValue(ex));
|
|
576
|
+
}
|
|
577
|
+
lines.push('');
|
|
578
|
+
}
|
|
579
|
+
|
|
538
580
|
// Subcommands section
|
|
539
581
|
if (info.subcommands && info.subcommands.length > 0) {
|
|
540
582
|
lines.push(...formatSubcommandsSection(info));
|
|
@@ -564,7 +606,7 @@ function createGenericFormatter(styler: Styler, layout: LayoutConfig): Formatter
|
|
|
564
606
|
|
|
565
607
|
// Nested commands section (full detail mode)
|
|
566
608
|
if (info.nestedCommands?.length) {
|
|
567
|
-
lines.push(styler.
|
|
609
|
+
lines.push(styler.section('Subcommand Details:'));
|
|
568
610
|
lines.push('');
|
|
569
611
|
for (const nestedCmd of info.nestedCommands) {
|
|
570
612
|
lines.push(styler.meta('─'.repeat(60)));
|
|
@@ -626,12 +668,18 @@ function createMinimalFormatter(): Formatter {
|
|
|
626
668
|
};
|
|
627
669
|
}
|
|
628
670
|
|
|
629
|
-
export function createFormatter(
|
|
671
|
+
export function createFormatter(
|
|
672
|
+
format: HelpFormat | 'auto',
|
|
673
|
+
detail: HelpDetail = 'standard',
|
|
674
|
+
theme?: ColorTheme | ColorConfig,
|
|
675
|
+
all?: boolean,
|
|
676
|
+
): Formatter {
|
|
630
677
|
if (detail === 'minimal') return createMinimalFormatter();
|
|
631
678
|
if (format === 'json') return createJsonFormatter();
|
|
632
|
-
if (format === 'ansi' || (format === 'auto' && shouldUseAnsi()))
|
|
633
|
-
|
|
634
|
-
if (format === '
|
|
635
|
-
if (format === '
|
|
636
|
-
return createGenericFormatter(
|
|
679
|
+
if (format === 'ansi' || (format === 'auto' && shouldUseAnsi()))
|
|
680
|
+
return createGenericFormatter(createAnsiStyler(theme), createTextLayout(), all);
|
|
681
|
+
if (format === 'console') return createGenericFormatter(createConsoleStyler(theme), createTextLayout(), all);
|
|
682
|
+
if (format === 'markdown') return createGenericFormatter(createMarkdownStyler(), createMarkdownLayout(), all);
|
|
683
|
+
if (format === 'html') return createGenericFormatter(createHtmlStyler(), createHtmlLayout(), all);
|
|
684
|
+
return createGenericFormatter(createTextStyler(), createTextLayout(), all);
|
|
637
685
|
}
|