padrone 1.4.0 → 1.6.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 +115 -0
- package/README.md +108 -283
- package/dist/args-Cnq0nwSM.mjs +272 -0
- package/dist/args-Cnq0nwSM.mjs.map +1 -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/commands-B_gufyR9.mjs +514 -0
- package/dist/commands-B_gufyR9.mjs.map +1 -0
- package/dist/{completion.mjs → completion-BEuflbDO.mjs} +86 -108
- package/dist/completion-BEuflbDO.mjs.map +1 -0
- package/dist/docs/index.d.mts +22 -2
- package/dist/docs/index.d.mts.map +1 -1
- package/dist/docs/index.mjs +92 -7
- package/dist/docs/index.mjs.map +1 -1
- package/dist/errors-CL63UOzt.mjs +137 -0
- package/dist/errors-CL63UOzt.mjs.map +1 -0
- package/dist/{formatter-ClUK5hcQ.d.mts → formatter-DrvhDMrq.d.mts} +35 -6
- package/dist/formatter-DrvhDMrq.d.mts.map +1 -0
- package/dist/help-B5Kk83of.mjs +849 -0
- package/dist/help-B5Kk83of.mjs.map +1 -0
- package/dist/index-BaU3X6dY.d.mts +1178 -0
- package/dist/index-BaU3X6dY.d.mts.map +1 -0
- package/dist/index.d.mts +763 -36
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +3608 -1534
- package/dist/index.mjs.map +1 -1
- package/dist/mcp-BM-d0nZi.mjs +377 -0
- package/dist/mcp-BM-d0nZi.mjs.map +1 -0
- package/dist/serve-Bk0JUlCj.mjs +402 -0
- package/dist/serve-Bk0JUlCj.mjs.map +1 -0
- package/dist/stream-DC4H8YTx.mjs +77 -0
- package/dist/stream-DC4H8YTx.mjs.map +1 -0
- package/dist/test.d.mts +5 -8
- package/dist/test.d.mts.map +1 -1
- package/dist/test.mjs +5 -27
- package/dist/test.mjs.map +1 -1
- package/dist/{update-check-EbNDkzyV.mjs → update-check-CZ2VqjnV.mjs} +16 -17
- package/dist/update-check-CZ2VqjnV.mjs.map +1 -0
- 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 +20 -9
- package/src/cli/completions.ts +14 -11
- package/src/cli/docs.ts +13 -16
- package/src/cli/doctor.ts +213 -24
- package/src/cli/index.ts +28 -82
- package/src/cli/init.ts +12 -10
- package/src/cli/link.ts +22 -18
- package/src/cli/wrap.ts +14 -11
- 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/core/args.ts +296 -0
- package/src/core/commands.ts +373 -0
- package/src/core/create.ts +268 -0
- package/src/{runtime.ts → core/default-runtime.ts} +70 -135
- package/src/{errors.ts → core/errors.ts} +22 -0
- package/src/core/exec.ts +259 -0
- package/src/core/interceptors.ts +302 -0
- package/src/{parse.ts → core/parse.ts} +36 -89
- package/src/core/program-methods.ts +301 -0
- package/src/core/results.ts +229 -0
- package/src/core/runtime.ts +246 -0
- package/src/core/validate.ts +247 -0
- package/src/docs/index.ts +124 -11
- package/src/extension/auto-output.ts +95 -0
- package/src/extension/color.ts +38 -0
- package/src/extension/completion.ts +49 -0
- package/src/extension/config.ts +262 -0
- package/src/extension/env.ts +101 -0
- package/src/extension/help.ts +192 -0
- package/src/extension/index.ts +43 -0
- package/src/extension/ink.ts +93 -0
- package/src/extension/interactive.ts +106 -0
- package/src/extension/logger.ts +214 -0
- package/src/extension/man.ts +51 -0
- package/src/extension/mcp.ts +52 -0
- package/src/extension/progress-renderer.ts +338 -0
- package/src/extension/progress.ts +299 -0
- package/src/extension/repl.ts +94 -0
- package/src/extension/serve.ts +48 -0
- package/src/extension/signal.ts +87 -0
- package/src/extension/stdin.ts +62 -0
- package/src/extension/suggestions.ts +114 -0
- package/src/extension/timing.ts +81 -0
- package/src/extension/tracing.ts +175 -0
- package/src/extension/update-check.ts +77 -0
- package/src/extension/utils.ts +51 -0
- package/src/extension/version.ts +63 -0
- package/src/{completion.ts → feature/completion.ts} +130 -57
- package/src/{interactive.ts → feature/interactive.ts} +47 -6
- package/src/feature/mcp.ts +387 -0
- package/src/{repl-loop.ts → feature/repl-loop.ts} +26 -16
- package/src/feature/serve.ts +438 -0
- package/src/feature/test.ts +262 -0
- package/src/{update-check.ts → feature/update-check.ts} +16 -16
- package/src/{wrap.ts → feature/wrap.ts} +27 -27
- package/src/index.ts +120 -11
- package/src/output/colorizer.ts +154 -0
- package/src/{formatter.ts → output/formatter.ts} +281 -135
- package/src/{help.ts → output/help.ts} +62 -15
- package/src/{zod.d.ts → schema/zod.d.ts} +1 -1
- package/src/schema/zod.ts +50 -0
- package/src/test.ts +2 -285
- package/src/types/args-meta.ts +151 -0
- package/src/types/builder.ts +697 -0
- package/src/types/command.ts +157 -0
- package/src/types/index.ts +59 -0
- package/src/types/interceptor.ts +296 -0
- package/src/types/preferences.ts +83 -0
- package/src/types/result.ts +71 -0
- package/src/types/schema.ts +19 -0
- package/src/util/dotenv.ts +244 -0
- package/src/{shell-utils.ts → util/shell-utils.ts} +26 -9
- package/src/util/stream.ts +101 -0
- package/src/{type-helpers.ts → util/type-helpers.ts} +23 -16
- package/src/{type-utils.ts → util/type-utils.ts} +99 -37
- package/src/util/utils.ts +51 -0
- package/src/zod.ts +1 -0
- package/dist/args-CVDbyyzG.mjs +0 -199
- package/dist/args-CVDbyyzG.mjs.map +0 -1
- package/dist/chunk-y_GBKt04.mjs +0 -5
- package/dist/completion.d.mts +0 -64
- package/dist/completion.d.mts.map +0 -1
- package/dist/completion.mjs.map +0 -1
- package/dist/formatter-ClUK5hcQ.d.mts.map +0 -1
- package/dist/help-CcBe91bV.mjs +0 -1254
- package/dist/help-CcBe91bV.mjs.map +0 -1
- package/dist/types-DjIdJN5G.d.mts +0 -1059
- package/dist/types-DjIdJN5G.d.mts.map +0 -1
- package/dist/update-check-EbNDkzyV.mjs.map +0 -1
- package/src/args.ts +0 -461
- package/src/colorizer.ts +0 -41
- package/src/command-utils.ts +0 -532
- package/src/create.ts +0 -1477
- package/src/types.ts +0 -1109
- package/src/utils.ts +0 -140
|
@@ -1,5 +1,24 @@
|
|
|
1
|
-
import { camelToKebab } from '
|
|
2
|
-
import { createColorizer } from './colorizer.ts';
|
|
1
|
+
import { camelToKebab } from '../util/shell-utils.ts';
|
|
2
|
+
import { type ColorConfig, type ColorTheme, createColorizer } from './colorizer.ts';
|
|
3
|
+
|
|
4
|
+
const DEFAULT_TERMINAL_WIDTH = 80;
|
|
5
|
+
|
|
6
|
+
function wrapText(text: string, maxWidth: number): string[] {
|
|
7
|
+
if (maxWidth <= 0 || text.length <= maxWidth) return [text];
|
|
8
|
+
const words = text.split(' ');
|
|
9
|
+
const lines: string[] = [];
|
|
10
|
+
let current = '';
|
|
11
|
+
for (const word of words) {
|
|
12
|
+
if (current && current.length + 1 + word.length > maxWidth) {
|
|
13
|
+
lines.push(current);
|
|
14
|
+
current = word;
|
|
15
|
+
} else {
|
|
16
|
+
current = current ? `${current} ${word}` : word;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
if (current) lines.push(current);
|
|
20
|
+
return lines.length > 0 ? lines : [text];
|
|
21
|
+
}
|
|
3
22
|
|
|
4
23
|
export type HelpFormat = 'text' | 'ansi' | 'console' | 'markdown' | 'html' | 'json';
|
|
5
24
|
export type HelpDetail = 'minimal' | 'standard' | 'full';
|
|
@@ -45,6 +64,8 @@ export type HelpArgumentInfo = {
|
|
|
45
64
|
negatable?: boolean;
|
|
46
65
|
/** Config file key that maps to this arg */
|
|
47
66
|
configKey?: string;
|
|
67
|
+
/** Group name for organizing this option under a labeled section in help output */
|
|
68
|
+
group?: string;
|
|
48
69
|
};
|
|
49
70
|
|
|
50
71
|
/**
|
|
@@ -58,6 +79,8 @@ export type HelpSubcommandInfo = {
|
|
|
58
79
|
deprecated?: boolean | string;
|
|
59
80
|
hidden?: boolean;
|
|
60
81
|
hasSubcommands?: boolean;
|
|
82
|
+
/** Group name for organizing this command under a labeled section in help output */
|
|
83
|
+
group?: string;
|
|
61
84
|
};
|
|
62
85
|
|
|
63
86
|
/**
|
|
@@ -103,6 +126,8 @@ export type HelpInfo = {
|
|
|
103
126
|
arguments?: HelpArgumentInfo[];
|
|
104
127
|
/** Built-in commands and flags (shown only for root command) */
|
|
105
128
|
builtins?: HelpBuiltinInfo[];
|
|
129
|
+
/** Command-level usage examples (shown in help output) */
|
|
130
|
+
examples?: string[];
|
|
106
131
|
/** Full help info for nested commands (used in 'full' detail mode) */
|
|
107
132
|
nestedCommands?: HelpInfo[];
|
|
108
133
|
};
|
|
@@ -133,6 +158,7 @@ type Styler = {
|
|
|
133
158
|
type: (text: string) => string;
|
|
134
159
|
description: (text: string) => string;
|
|
135
160
|
label: (text: string) => string;
|
|
161
|
+
section: (text: string) => string;
|
|
136
162
|
meta: (text: string) => string;
|
|
137
163
|
example: (text: string) => string;
|
|
138
164
|
exampleValue: (text: string) => string;
|
|
@@ -147,7 +173,6 @@ type LayoutConfig = {
|
|
|
147
173
|
indent: (level: number) => string;
|
|
148
174
|
join: (parts: string[]) => string;
|
|
149
175
|
wrapDocument?: (content: string) => string;
|
|
150
|
-
usageLabel: string;
|
|
151
176
|
};
|
|
152
177
|
|
|
153
178
|
// ============================================================================
|
|
@@ -161,6 +186,7 @@ function createTextStyler(): Styler {
|
|
|
161
186
|
type: (text) => text,
|
|
162
187
|
description: (text) => text,
|
|
163
188
|
label: (text) => text,
|
|
189
|
+
section: (text) => text,
|
|
164
190
|
meta: (text) => text,
|
|
165
191
|
example: (text) => text,
|
|
166
192
|
exampleValue: (text) => text,
|
|
@@ -168,14 +194,15 @@ function createTextStyler(): Styler {
|
|
|
168
194
|
};
|
|
169
195
|
}
|
|
170
196
|
|
|
171
|
-
function createAnsiStyler(): Styler {
|
|
172
|
-
const colorizer = createColorizer();
|
|
197
|
+
function createAnsiStyler(theme?: ColorTheme | ColorConfig): Styler {
|
|
198
|
+
const colorizer = createColorizer(theme);
|
|
173
199
|
return {
|
|
174
200
|
command: colorizer.command,
|
|
175
201
|
arg: colorizer.arg,
|
|
176
202
|
type: colorizer.type,
|
|
177
203
|
description: colorizer.description,
|
|
178
204
|
label: colorizer.label,
|
|
205
|
+
section: colorizer.label,
|
|
179
206
|
meta: colorizer.meta,
|
|
180
207
|
example: colorizer.example,
|
|
181
208
|
exampleValue: colorizer.exampleValue,
|
|
@@ -183,30 +210,8 @@ function createAnsiStyler(): Styler {
|
|
|
183
210
|
};
|
|
184
211
|
}
|
|
185
212
|
|
|
186
|
-
function createConsoleStyler(): Styler {
|
|
187
|
-
|
|
188
|
-
reset: '\x1b[0m',
|
|
189
|
-
bold: '\x1b[1m',
|
|
190
|
-
dim: '\x1b[2m',
|
|
191
|
-
italic: '\x1b[3m',
|
|
192
|
-
underline: '\x1b[4m',
|
|
193
|
-
strikethrough: '\x1b[9m',
|
|
194
|
-
cyan: '\x1b[36m',
|
|
195
|
-
green: '\x1b[32m',
|
|
196
|
-
yellow: '\x1b[33m',
|
|
197
|
-
gray: '\x1b[90m',
|
|
198
|
-
};
|
|
199
|
-
return {
|
|
200
|
-
command: (text) => `${colors.cyan}${colors.bold}${text}${colors.reset}`,
|
|
201
|
-
arg: (text) => `${colors.green}${text}${colors.reset}`,
|
|
202
|
-
type: (text) => `${colors.yellow}${text}${colors.reset}`,
|
|
203
|
-
description: (text) => `${colors.dim}${text}${colors.reset}`,
|
|
204
|
-
label: (text) => `${colors.bold}${text}${colors.reset}`,
|
|
205
|
-
meta: (text) => `${colors.gray}${text}${colors.reset}`,
|
|
206
|
-
example: (text) => `${colors.underline}${text}${colors.reset}`,
|
|
207
|
-
exampleValue: (text) => `${colors.italic}${text}${colors.reset}`,
|
|
208
|
-
deprecated: (text) => `${colors.strikethrough}${colors.gray}${text}${colors.reset}`,
|
|
209
|
-
};
|
|
213
|
+
function createConsoleStyler(theme?: ColorTheme | ColorConfig): Styler {
|
|
214
|
+
return createAnsiStyler(theme);
|
|
210
215
|
}
|
|
211
216
|
|
|
212
217
|
function createMarkdownStyler(): Styler {
|
|
@@ -215,7 +220,8 @@ function createMarkdownStyler(): Styler {
|
|
|
215
220
|
arg: (text) => `\`${text}\``,
|
|
216
221
|
type: (text) => `\`${text}\``,
|
|
217
222
|
description: (text) => text,
|
|
218
|
-
label: (text) =>
|
|
223
|
+
label: (text) => `**${text}**`,
|
|
224
|
+
section: (text) => `### ${text}`,
|
|
219
225
|
meta: (text) => `*${text}*`,
|
|
220
226
|
example: (text) => `**${text}**`,
|
|
221
227
|
exampleValue: (text) => `\`${text}\``,
|
|
@@ -233,7 +239,8 @@ function createHtmlStyler(): Styler {
|
|
|
233
239
|
arg: (text) => `<code style="color: #4caf50;">${escapeHtml(text)}</code>`,
|
|
234
240
|
type: (text) => `<code style="color: #ff9800;">${escapeHtml(text)}</code>`,
|
|
235
241
|
description: (text) => `<span style="color: #666;">${escapeHtml(text)}</span>`,
|
|
236
|
-
label: (text) => `<
|
|
242
|
+
label: (text) => `<strong>${escapeHtml(text)}</strong>`,
|
|
243
|
+
section: (text) => `<h3>${escapeHtml(text)}</h3>`,
|
|
237
244
|
meta: (text) => `<span style="color: #999;">${escapeHtml(text)}</span>`,
|
|
238
245
|
example: (text) => `<strong style="text-decoration: underline;">${escapeHtml(text)}</strong>`,
|
|
239
246
|
exampleValue: (text) => `<em>${escapeHtml(text)}</em>`,
|
|
@@ -250,7 +257,6 @@ function createTextLayout(): LayoutConfig {
|
|
|
250
257
|
newline: '\n',
|
|
251
258
|
indent: (level) => ' '.repeat(level),
|
|
252
259
|
join: (parts) => parts.filter(Boolean).join(' '),
|
|
253
|
-
usageLabel: 'Usage:',
|
|
254
260
|
};
|
|
255
261
|
}
|
|
256
262
|
|
|
@@ -263,7 +269,6 @@ function createMarkdownLayout(): LayoutConfig {
|
|
|
263
269
|
return ' ';
|
|
264
270
|
},
|
|
265
271
|
join: (parts) => parts.filter(Boolean).join(' '),
|
|
266
|
-
usageLabel: 'Usage:',
|
|
267
272
|
};
|
|
268
273
|
}
|
|
269
274
|
|
|
@@ -273,7 +278,6 @@ function createHtmlLayout(): LayoutConfig {
|
|
|
273
278
|
indent: (level) => ' '.repeat(level),
|
|
274
279
|
join: (parts) => parts.filter(Boolean).join(' '),
|
|
275
280
|
wrapDocument: (content) => `<div style="font-family: monospace; line-height: 1.6;">${content}</div>`,
|
|
276
|
-
usageLabel: '<strong>Usage:</strong>',
|
|
277
281
|
};
|
|
278
282
|
}
|
|
279
283
|
|
|
@@ -284,8 +288,8 @@ function createHtmlLayout(): LayoutConfig {
|
|
|
284
288
|
/**
|
|
285
289
|
* Creates a formatter that uses the given styler and layout configuration.
|
|
286
290
|
*/
|
|
287
|
-
function createGenericFormatter(styler: Styler, layout: LayoutConfig): Formatter {
|
|
288
|
-
const { newline, indent, join, wrapDocument
|
|
291
|
+
function createGenericFormatter(styler: Styler, layout: LayoutConfig, showAllBuiltins?: boolean, terminalWidth?: number): Formatter {
|
|
292
|
+
const { newline, indent, join, wrapDocument } = layout;
|
|
289
293
|
|
|
290
294
|
function formatUsageSection(info: HelpInfo): string[] {
|
|
291
295
|
const usageParts: string[] = [styler.command(info.usage.command), info.usage.hasSubcommands ? styler.meta('[command]') : ''];
|
|
@@ -297,15 +301,13 @@ function createGenericFormatter(styler: Styler, layout: LayoutConfig): Formatter
|
|
|
297
301
|
}
|
|
298
302
|
}
|
|
299
303
|
if (info.usage.hasArguments) usageParts.push(styler.meta('[options]'));
|
|
300
|
-
return [`${
|
|
304
|
+
return [`${styler.label('Usage:')} ${join(usageParts)}`];
|
|
301
305
|
}
|
|
302
306
|
|
|
303
307
|
function formatSubcommandsSection(info: HelpInfo): string[] {
|
|
304
308
|
const lines: string[] = [];
|
|
305
309
|
const subcommands = info.subcommands!;
|
|
306
310
|
|
|
307
|
-
lines.push(styler.label('Commands:'));
|
|
308
|
-
|
|
309
311
|
const subcommandSuffix = (c: HelpSubcommandInfo) => (c.hasSubcommands ? ' <subcommand>' : '');
|
|
310
312
|
const formatAliasParts = (c: HelpSubcommandInfo) => {
|
|
311
313
|
if (!c.aliases?.length) return { plain: '', styled: '' };
|
|
@@ -323,40 +325,55 @@ function createGenericFormatter(styler: Styler, layout: LayoutConfig): Formatter
|
|
|
323
325
|
}
|
|
324
326
|
return { plain: parts.length ? ` ${parts.join(' ')}` : '', styled: styledParts.length ? ` ${styledParts.join(' ')}` : '' };
|
|
325
327
|
};
|
|
328
|
+
// Column width is computed across all subcommands for consistent alignment
|
|
326
329
|
const maxNameLength = Math.max(
|
|
327
330
|
...subcommands.map((c) => {
|
|
328
331
|
return (c.name + subcommandSuffix(c) + formatAliasParts(c).plain).length;
|
|
329
332
|
}),
|
|
330
333
|
);
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
334
|
+
|
|
335
|
+
const grouped = Object.groupBy(subcommands, (c) => c.group ?? '');
|
|
336
|
+
|
|
337
|
+
const renderSubcommands = (cmds: HelpSubcommandInfo[]) => {
|
|
338
|
+
for (const subCmd of cmds) {
|
|
339
|
+
const aliasParts = formatAliasParts(subCmd);
|
|
340
|
+
const suffix = subcommandSuffix(subCmd);
|
|
341
|
+
const commandDisplay = subCmd.name + suffix + aliasParts.plain;
|
|
342
|
+
const padding = ' '.repeat(Math.max(0, maxNameLength - commandDisplay.length + 2));
|
|
343
|
+
const isDeprecated = !!subCmd.deprecated;
|
|
344
|
+
const isDefaultEntry = subCmd.name === '[default]';
|
|
345
|
+
const commandName = isDeprecated
|
|
346
|
+
? styler.deprecated(commandDisplay)
|
|
347
|
+
: (isDefaultEntry ? styler.meta(subCmd.name) : styler.command(subCmd.name)) +
|
|
348
|
+
(suffix ? styler.meta(suffix) : '') +
|
|
349
|
+
aliasParts.styled;
|
|
350
|
+
const lineParts: string[] = [commandName, padding];
|
|
351
|
+
|
|
352
|
+
// Use title if available, otherwise use description
|
|
353
|
+
const displayText = subCmd.title ?? subCmd.description;
|
|
354
|
+
if (displayText) {
|
|
355
|
+
lineParts.push(isDeprecated ? styler.deprecated(displayText) : styler.description(displayText));
|
|
356
|
+
}
|
|
357
|
+
if (isDeprecated) {
|
|
358
|
+
const deprecatedMeta =
|
|
359
|
+
typeof subCmd.deprecated === 'string' ? styler.meta(` (deprecated: ${subCmd.deprecated})`) : styler.meta(' (deprecated)');
|
|
360
|
+
lineParts.push(deprecatedMeta);
|
|
361
|
+
}
|
|
362
|
+
lines.push(indent(1) + lineParts.join(''));
|
|
354
363
|
}
|
|
355
|
-
|
|
364
|
+
};
|
|
365
|
+
|
|
366
|
+
for (const [key, items] of Object.entries(grouped)) {
|
|
367
|
+
if (lines.length > 0) lines.push('');
|
|
368
|
+
lines.push(styler.section(key ? `${key}:` : 'Commands:'));
|
|
369
|
+
renderSubcommands(items!);
|
|
356
370
|
}
|
|
357
371
|
|
|
358
|
-
|
|
359
|
-
|
|
372
|
+
// Skip hint when builtins are present — the builtins section shows a combined hint
|
|
373
|
+
if (!info.builtins?.length) {
|
|
374
|
+
lines.push('');
|
|
375
|
+
lines.push(styler.meta(`Run "${info.name} [command] --help" for more information on a command.`));
|
|
376
|
+
}
|
|
360
377
|
|
|
361
378
|
return lines;
|
|
362
379
|
}
|
|
@@ -365,18 +382,56 @@ function createGenericFormatter(styler: Styler, layout: LayoutConfig): Formatter
|
|
|
365
382
|
const lines: string[] = [];
|
|
366
383
|
const args = info.positionals!;
|
|
367
384
|
|
|
368
|
-
lines.push(styler.
|
|
385
|
+
lines.push(styler.section('Arguments:'));
|
|
369
386
|
|
|
370
387
|
const maxNameLength = Math.min(32, Math.max(...args.map((a) => a.name.length)));
|
|
388
|
+
const descCol = 2 + maxNameLength + 2;
|
|
389
|
+
const posAvailWidth = terminalWidth ? terminalWidth - descCol : undefined;
|
|
390
|
+
const descColPad = ' '.repeat(descCol);
|
|
371
391
|
|
|
372
392
|
for (const arg of args) {
|
|
373
393
|
const padding = ' '.repeat(Math.max(2, maxNameLength - arg.name.length + 2));
|
|
374
|
-
const
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
394
|
+
const prefix = indent(1) + styler.arg(arg.name) + padding;
|
|
395
|
+
|
|
396
|
+
const descPlain = arg.description ?? '';
|
|
397
|
+
const styledDesc = descPlain ? styler.description(descPlain) : '';
|
|
398
|
+
|
|
399
|
+
const metaParts: string[] = [];
|
|
400
|
+
const styledMetaParts: string[] = [];
|
|
401
|
+
if (info.usage.stdinField === arg.name) {
|
|
402
|
+
metaParts.push('(stdin)');
|
|
403
|
+
styledMetaParts.push(styler.meta('(stdin)'));
|
|
404
|
+
}
|
|
405
|
+
if (arg.enum) {
|
|
406
|
+
const text = `(choices: ${arg.enum.join(', ')})`;
|
|
407
|
+
metaParts.push(text);
|
|
408
|
+
styledMetaParts.push(styler.meta(text));
|
|
409
|
+
}
|
|
410
|
+
if (arg.default !== undefined) {
|
|
411
|
+
const text = `(default: ${String(arg.default)})`;
|
|
412
|
+
metaParts.push(text);
|
|
413
|
+
styledMetaParts.push(styler.meta(text));
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
const metaStyled = join(styledMetaParts);
|
|
417
|
+
|
|
418
|
+
if (posAvailWidth && posAvailWidth > 0) {
|
|
419
|
+
const metaPlain = metaParts.join(' ');
|
|
420
|
+
const fullPlain = [descPlain, metaPlain].filter(Boolean).join(' ');
|
|
421
|
+
if (fullPlain.length <= posAvailWidth) {
|
|
422
|
+
lines.push(prefix + [styledDesc, metaStyled].filter(Boolean).join(' '));
|
|
423
|
+
} else if (!descPlain || descPlain.length <= posAvailWidth) {
|
|
424
|
+
lines.push(prefix + styledDesc);
|
|
425
|
+
if (metaStyled) lines.push(descColPad + metaStyled);
|
|
426
|
+
} else {
|
|
427
|
+
const wrapped = wrapText(descPlain, posAvailWidth);
|
|
428
|
+
lines.push(prefix + styler.description(wrapped[0]!));
|
|
429
|
+
for (const wline of wrapped.slice(1)) lines.push(descColPad + styler.description(wline));
|
|
430
|
+
if (metaStyled) lines.push(descColPad + metaStyled);
|
|
431
|
+
}
|
|
432
|
+
} else {
|
|
433
|
+
lines.push(prefix + join([styledDesc, metaStyled]));
|
|
434
|
+
}
|
|
380
435
|
}
|
|
381
436
|
|
|
382
437
|
return lines;
|
|
@@ -386,8 +441,6 @@ function createGenericFormatter(styler: Styler, layout: LayoutConfig): Formatter
|
|
|
386
441
|
const lines: string[] = [];
|
|
387
442
|
const argList = info.arguments || [];
|
|
388
443
|
|
|
389
|
-
lines.push(styler.label('Options:'));
|
|
390
|
-
|
|
391
444
|
// Helper to check if a default value is meaningful (not empty string/array)
|
|
392
445
|
const hasDefault = (value: unknown): boolean => {
|
|
393
446
|
if (value === undefined) return false;
|
|
@@ -396,70 +449,134 @@ function createGenericFormatter(styler: Styler, layout: LayoutConfig): Formatter
|
|
|
396
449
|
return true;
|
|
397
450
|
};
|
|
398
451
|
|
|
399
|
-
// Build
|
|
400
|
-
const argColumns
|
|
452
|
+
// Build columns: flags | names | type | description
|
|
453
|
+
const argColumns = argList.map((arg) => {
|
|
401
454
|
// Promote kebab-case alias to primary display name if it exists
|
|
402
455
|
const kebab = camelToKebab(arg.name);
|
|
403
456
|
const primaryName = kebab && arg.aliases?.includes(kebab) ? kebab : arg.name;
|
|
404
457
|
const remainingAliases = arg.aliases?.filter((a) => a !== primaryName);
|
|
405
|
-
const argName = `--${primaryName}`;
|
|
406
|
-
const flagNames = arg.flags?.length ? arg.flags.map((f) => `-${f}`).join(', ') : '';
|
|
407
|
-
const aliasNames = remainingAliases?.length ? remainingAliases.map((a) => `--${a}`).join(', ') : '';
|
|
408
|
-
const shortNames = [flagNames, aliasNames].filter(Boolean).join(', ');
|
|
409
|
-
const fullArgName = shortNames ? `${argName}, ${shortNames}` : argName;
|
|
410
|
-
const isDeprecated = !!arg.deprecated;
|
|
411
|
-
const formattedArgName = isDeprecated ? styler.deprecated(fullArgName) : styler.arg(fullArgName);
|
|
412
458
|
|
|
413
|
-
const
|
|
414
|
-
const
|
|
459
|
+
const flagsPlain = arg.flags?.length ? arg.flags.map((f) => `-${f}`).join(', ') : '';
|
|
460
|
+
const namesPlain = [`--${primaryName}`, ...(remainingAliases?.map((a) => `--${a}`) || [])].join(', ');
|
|
461
|
+
const typePlain = arg.type && arg.type !== 'boolean' ? (arg.optional ? `[${arg.type}]` : `<${arg.type}>`) : '';
|
|
415
462
|
|
|
416
|
-
|
|
417
|
-
const typePart = arg.optional ? `[${arg.type}]` : `<${arg.type}>`;
|
|
418
|
-
plainParts.push(typePart);
|
|
419
|
-
styledParts.push(styler.type(typePart));
|
|
420
|
-
}
|
|
421
|
-
if (isDeprecated) {
|
|
422
|
-
const deprecatedPart = typeof arg.deprecated === 'string' ? `(deprecated: ${arg.deprecated})` : '(deprecated)';
|
|
423
|
-
plainParts.push(deprecatedPart);
|
|
424
|
-
styledParts.push(styler.meta(deprecatedPart));
|
|
425
|
-
}
|
|
463
|
+
const isDeprecated = !!arg.deprecated;
|
|
426
464
|
|
|
427
|
-
return {
|
|
465
|
+
return { flagsPlain, namesPlain, typePlain, isDeprecated, arg };
|
|
428
466
|
});
|
|
429
467
|
|
|
430
|
-
|
|
468
|
+
// Column widths are computed across all args (regardless of group) for consistent alignment
|
|
469
|
+
const maxFlagsWidth = Math.min(12, Math.max(0, ...argColumns.map((c) => c.flagsPlain.length)));
|
|
470
|
+
const maxNamesWidth = Math.min(32, Math.max(0, ...argColumns.map((c) => c.namesPlain.length)));
|
|
471
|
+
const maxTypeWidth = Math.min(16, Math.max(0, ...argColumns.map((c) => c.typePlain.length)));
|
|
472
|
+
const hasAnyFlags = maxFlagsWidth > 0;
|
|
473
|
+
const descCol = 2 + (hasAnyFlags ? maxFlagsWidth + 2 : 0) + maxNamesWidth + 2 + (maxTypeWidth > 0 ? maxTypeWidth + 2 : 0);
|
|
474
|
+
const argAvailWidth = terminalWidth ? terminalWidth - descCol : undefined;
|
|
475
|
+
const descColPad = ' '.repeat(descCol);
|
|
476
|
+
|
|
477
|
+
// Split into ordered groups: ungrouped first as "Options:", then each group in first-seen order
|
|
478
|
+
const grouped = Object.groupBy(argColumns, (c) => c.arg.group ?? '');
|
|
479
|
+
|
|
480
|
+
const renderArgColumns = (columns: typeof argColumns) => {
|
|
481
|
+
for (const { flagsPlain, namesPlain, typePlain, isDeprecated, arg } of columns) {
|
|
482
|
+
const parts: string[] = [];
|
|
483
|
+
|
|
484
|
+
// Flags column
|
|
485
|
+
if (hasAnyFlags) {
|
|
486
|
+
const styledFlags = isDeprecated ? (flagsPlain ? styler.deprecated(flagsPlain) : '') : flagsPlain ? styler.arg(flagsPlain) : '';
|
|
487
|
+
const flagsPadding = ' '.repeat(Math.max(0, maxFlagsWidth - flagsPlain.length));
|
|
488
|
+
const separator = flagsPlain ? ', ' : ' ';
|
|
489
|
+
parts.push(styledFlags + flagsPadding + separator);
|
|
490
|
+
}
|
|
431
491
|
|
|
432
|
-
|
|
433
|
-
|
|
492
|
+
// Names column
|
|
493
|
+
const styledNames = isDeprecated ? styler.deprecated(namesPlain) : styler.arg(namesPlain);
|
|
494
|
+
const namesPadding = ' '.repeat(Math.max(2, maxNamesWidth - namesPlain.length + 2));
|
|
495
|
+
parts.push(styledNames + namesPadding);
|
|
434
496
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
497
|
+
// Type column
|
|
498
|
+
if (maxTypeWidth > 0) {
|
|
499
|
+
const styledType = typePlain ? styler.type(typePlain) : '';
|
|
500
|
+
const typePadding = ' '.repeat(Math.max(2, maxTypeWidth - typePlain.length + 2));
|
|
501
|
+
parts.push(styledType + typePadding);
|
|
502
|
+
}
|
|
441
503
|
|
|
442
|
-
|
|
504
|
+
const prefix = indent(1) + parts.join('');
|
|
505
|
+
const contPad = argAvailWidth ? descColPad : indent(3);
|
|
443
506
|
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
const
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
507
|
+
// Build inline meta (deprecated no-reason, default, choices)
|
|
508
|
+
const inlineMeta: string[] = [];
|
|
509
|
+
const styledInlineMeta: string[] = [];
|
|
510
|
+
if (isDeprecated && typeof arg.deprecated !== 'string') {
|
|
511
|
+
inlineMeta.push('(deprecated)');
|
|
512
|
+
styledInlineMeta.push(styler.meta('(deprecated)'));
|
|
513
|
+
}
|
|
514
|
+
if (hasDefault(arg.default)) {
|
|
515
|
+
const text = `(default: ${String(arg.default)})`;
|
|
516
|
+
inlineMeta.push(text);
|
|
517
|
+
styledInlineMeta.push(styler.meta(text));
|
|
518
|
+
}
|
|
519
|
+
if (arg.enum) {
|
|
520
|
+
const text = `(choices: ${arg.enum.join(', ')})`;
|
|
521
|
+
inlineMeta.push(text);
|
|
522
|
+
styledInlineMeta.push(styler.meta(text));
|
|
523
|
+
}
|
|
450
524
|
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
const
|
|
454
|
-
|
|
455
|
-
|
|
525
|
+
const descPlain = arg.description ?? '';
|
|
526
|
+
const styledDesc = descPlain ? (isDeprecated ? styler.deprecated(descPlain) : styler.description(descPlain)) : '';
|
|
527
|
+
const metaStyled = join(styledInlineMeta);
|
|
528
|
+
|
|
529
|
+
if (argAvailWidth && argAvailWidth > 0) {
|
|
530
|
+
// Terminal-width-aware: try to fit description + meta on one line
|
|
531
|
+
const metaPlain = inlineMeta.join(' ');
|
|
532
|
+
const fullPlain = [descPlain, metaPlain].filter(Boolean).join(' ');
|
|
533
|
+
if (fullPlain.length <= argAvailWidth) {
|
|
534
|
+
lines.push(prefix + [styledDesc, metaStyled].filter(Boolean).join(' '));
|
|
535
|
+
} else if (!descPlain || descPlain.length <= argAvailWidth) {
|
|
536
|
+
lines.push(prefix + styledDesc);
|
|
537
|
+
if (metaStyled) lines.push(descColPad + metaStyled);
|
|
538
|
+
} else {
|
|
539
|
+
const wrapped = wrapText(descPlain, argAvailWidth);
|
|
540
|
+
const styleFn = isDeprecated ? styler.deprecated : styler.description;
|
|
541
|
+
lines.push(prefix + styleFn(wrapped[0]!));
|
|
542
|
+
for (const wline of wrapped.slice(1)) lines.push(descColPad + styleFn(wline));
|
|
543
|
+
if (metaStyled) lines.push(descColPad + metaStyled);
|
|
544
|
+
}
|
|
545
|
+
} else {
|
|
546
|
+
// No terminal width (markdown/html): description on line 1, meta on line 2
|
|
547
|
+
const descParts: string[] = [];
|
|
548
|
+
if (styledDesc) descParts.push(styledDesc);
|
|
549
|
+
lines.push(prefix + join(descParts));
|
|
550
|
+
if (styledInlineMeta.length > 0) lines.push(indent(3) + metaStyled);
|
|
551
|
+
}
|
|
456
552
|
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
553
|
+
// Deprecated (with reason), examples — always on separate line
|
|
554
|
+
const line3Parts: string[] = [];
|
|
555
|
+
if (isDeprecated && typeof arg.deprecated === 'string') line3Parts.push(styler.meta(`(deprecated: ${arg.deprecated})`));
|
|
556
|
+
if (arg.examples && arg.examples.length > 0) {
|
|
557
|
+
const exampleValues = arg.examples.map((example) => (typeof example === 'string' ? example : JSON.stringify(example))).join(', ');
|
|
558
|
+
line3Parts.push(styler.example('Example:'), styler.exampleValue(exampleValues));
|
|
559
|
+
}
|
|
560
|
+
if (line3Parts.length > 0) lines.push(contPad + join(line3Parts));
|
|
561
|
+
|
|
562
|
+
// stdin, env, config — always on separate line
|
|
563
|
+
const line4Parts: string[] = [];
|
|
564
|
+
if (info.usage.stdinField === arg.name) line4Parts.push(styler.meta('(stdin)'));
|
|
565
|
+
if (arg.env) {
|
|
566
|
+
const envVars = typeof arg.env === 'string' ? [arg.env] : arg.env;
|
|
567
|
+
line4Parts.push(styler.example('Env:'), styler.exampleValue(envVars.join(', ')));
|
|
568
|
+
}
|
|
569
|
+
if (arg.configKey) {
|
|
570
|
+
line4Parts.push(styler.example('Config:'), styler.exampleValue(arg.configKey));
|
|
571
|
+
}
|
|
572
|
+
if (line4Parts.length > 0) lines.push(contPad + join(line4Parts));
|
|
462
573
|
}
|
|
574
|
+
};
|
|
575
|
+
|
|
576
|
+
for (const [key, items] of Object.entries(grouped)) {
|
|
577
|
+
if (lines.length > 0) lines.push('');
|
|
578
|
+
lines.push(styler.section(key ? `${key}:` : 'Options:'));
|
|
579
|
+
renderArgColumns(items!);
|
|
463
580
|
}
|
|
464
581
|
|
|
465
582
|
return lines;
|
|
@@ -469,7 +586,18 @@ function createGenericFormatter(styler: Styler, layout: LayoutConfig): Formatter
|
|
|
469
586
|
const lines: string[] = [];
|
|
470
587
|
const builtins = info.builtins!;
|
|
471
588
|
|
|
472
|
-
|
|
589
|
+
if (!showAllBuiltins) {
|
|
590
|
+
// Collapsed summary: show selected builtins on a single line with a combined hint
|
|
591
|
+
const highlights = ['help [command]', 'version', '[command] --repl'];
|
|
592
|
+
lines.push(`${styler.label('Global:')} ${styler.meta(highlights.join(', '))}`);
|
|
593
|
+
const hint = info.usage.hasSubcommands
|
|
594
|
+
? `Run "${info.name} [command] --help" for more information. Use "--all" for all global commands.`
|
|
595
|
+
: `Use "${info.name} help --all" for more information on global commands.`;
|
|
596
|
+
lines.push(styler.meta(hint));
|
|
597
|
+
return lines;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
lines.push(styler.section('Global:'));
|
|
473
601
|
|
|
474
602
|
// Compute max effective name length for alignment across main and sub entries
|
|
475
603
|
const allLengths: number[] = [];
|
|
@@ -537,6 +665,15 @@ function createGenericFormatter(styler: Styler, layout: LayoutConfig): Formatter
|
|
|
537
665
|
lines.push('');
|
|
538
666
|
}
|
|
539
667
|
|
|
668
|
+
// Examples section (if present)
|
|
669
|
+
if (info.examples && info.examples.length > 0) {
|
|
670
|
+
lines.push(styler.section('Examples:'));
|
|
671
|
+
for (const ex of info.examples) {
|
|
672
|
+
lines.push(indent(1) + styler.meta('$ ') + styler.exampleValue(ex));
|
|
673
|
+
}
|
|
674
|
+
lines.push('');
|
|
675
|
+
}
|
|
676
|
+
|
|
540
677
|
// Subcommands section
|
|
541
678
|
if (info.subcommands && info.subcommands.length > 0) {
|
|
542
679
|
lines.push(...formatSubcommandsSection(info));
|
|
@@ -566,7 +703,7 @@ function createGenericFormatter(styler: Styler, layout: LayoutConfig): Formatter
|
|
|
566
703
|
|
|
567
704
|
// Nested commands section (full detail mode)
|
|
568
705
|
if (info.nestedCommands?.length) {
|
|
569
|
-
lines.push(styler.
|
|
706
|
+
lines.push(styler.section('Subcommand Details:'));
|
|
570
707
|
lines.push('');
|
|
571
708
|
for (const nestedCmd of info.nestedCommands) {
|
|
572
709
|
lines.push(styler.meta('─'.repeat(60)));
|
|
@@ -596,11 +733,10 @@ function createJsonFormatter(): Formatter {
|
|
|
596
733
|
// Formatter Factory
|
|
597
734
|
// ============================================================================
|
|
598
735
|
|
|
599
|
-
function shouldUseAnsi(): boolean {
|
|
600
|
-
if (
|
|
601
|
-
if (
|
|
602
|
-
if (
|
|
603
|
-
if (process.stdout && typeof process.stdout.isTTY === 'boolean') return process.stdout.isTTY;
|
|
736
|
+
function shouldUseAnsi(env?: Record<string, string | undefined>, isTTY?: boolean): boolean {
|
|
737
|
+
if (env?.NO_COLOR) return false;
|
|
738
|
+
if (env?.CI) return false;
|
|
739
|
+
if (typeof isTTY === 'boolean') return isTTY;
|
|
604
740
|
return false;
|
|
605
741
|
}
|
|
606
742
|
|
|
@@ -628,12 +764,22 @@ function createMinimalFormatter(): Formatter {
|
|
|
628
764
|
};
|
|
629
765
|
}
|
|
630
766
|
|
|
631
|
-
export function createFormatter(
|
|
767
|
+
export function createFormatter(
|
|
768
|
+
format: HelpFormat | 'auto',
|
|
769
|
+
detail: HelpDetail = 'standard',
|
|
770
|
+
theme?: ColorTheme | ColorConfig,
|
|
771
|
+
all?: boolean,
|
|
772
|
+
width?: number,
|
|
773
|
+
terminal?: { columns?: number; isTTY?: boolean },
|
|
774
|
+
env?: Record<string, string | undefined>,
|
|
775
|
+
): Formatter {
|
|
632
776
|
if (detail === 'minimal') return createMinimalFormatter();
|
|
633
777
|
if (format === 'json') return createJsonFormatter();
|
|
634
|
-
|
|
635
|
-
if (format === '
|
|
636
|
-
|
|
637
|
-
if (format === '
|
|
638
|
-
return createGenericFormatter(
|
|
778
|
+
const tw = format === 'markdown' || format === 'html' ? undefined : (width ?? terminal?.columns ?? DEFAULT_TERMINAL_WIDTH);
|
|
779
|
+
if (format === 'ansi' || (format === 'auto' && shouldUseAnsi(env, terminal?.isTTY)))
|
|
780
|
+
return createGenericFormatter(createAnsiStyler(theme), createTextLayout(), all, tw);
|
|
781
|
+
if (format === 'console') return createGenericFormatter(createConsoleStyler(theme), createTextLayout(), all, tw);
|
|
782
|
+
if (format === 'markdown') return createGenericFormatter(createMarkdownStyler(), createMarkdownLayout(), all);
|
|
783
|
+
if (format === 'html') return createGenericFormatter(createHtmlStyler(), createHtmlLayout(), all);
|
|
784
|
+
return createGenericFormatter(createTextStyler(), createTextLayout(), all, tw);
|
|
639
785
|
}
|