padrone 1.5.0 → 1.7.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 +44 -0
- package/README.md +15 -11
- package/dist/{args-D5PNDyNu.mjs → args-Cnq0nwSM.mjs} +91 -41
- package/dist/args-Cnq0nwSM.mjs.map +1 -0
- package/dist/codegen/index.mjs +4 -4
- 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} +12 -82
- package/dist/completion-BEuflbDO.mjs.map +1 -0
- package/dist/docs/index.d.mts +4 -4
- package/dist/docs/index.d.mts.map +1 -1
- package/dist/docs/index.mjs +10 -12
- package/dist/docs/index.mjs.map +1 -1
- package/dist/{errors-BiVrBgi6.mjs → errors-DA4KzK1M.mjs} +26 -3
- package/dist/errors-DA4KzK1M.mjs.map +1 -0
- package/dist/{formatter-DtHzbP22.d.mts → formatter-DrvhDMrq.d.mts} +3 -3
- package/dist/formatter-DrvhDMrq.d.mts.map +1 -0
- package/dist/{help-bbmu9-qd.mjs → help-BtxLgrF_.mjs} +190 -43
- package/dist/help-BtxLgrF_.mjs.map +1 -0
- package/dist/{types-Ch8Mk6Qb.d.mts → index-D6-7dz0l.d.mts} +634 -745
- package/dist/index-D6-7dz0l.d.mts.map +1 -0
- package/dist/index.d.mts +869 -36
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +3884 -1699
- package/dist/index.mjs.map +1 -1
- package/dist/{mcp-mLWIdUIu.mjs → mcp-6-Jw4Bpq.mjs} +13 -15
- package/dist/mcp-6-Jw4Bpq.mjs.map +1 -0
- package/dist/{serve-B0u43DK7.mjs → serve-YVTPzBCl.mjs} +12 -14
- package/dist/serve-YVTPzBCl.mjs.map +1 -0
- package/dist/{stream-BcC146Ud.mjs → stream-DC4H8YTx.mjs} +24 -3
- 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 +2 -13
- package/dist/test.mjs.map +1 -1
- package/dist/{update-check-CFX1FV3v.mjs → update-check-CZ2VqjnV.mjs} +16 -17
- package/dist/update-check-CZ2VqjnV.mjs.map +1 -0
- package/dist/zod.d.mts +2 -2
- package/dist/zod.d.mts.map +1 -1
- package/dist/zod.mjs +2 -2
- package/dist/zod.mjs.map +1 -1
- package/package.json +15 -12
- package/src/cli/completions.ts +14 -11
- package/src/cli/docs.ts +13 -10
- package/src/cli/doctor.ts +22 -18
- package/src/cli/index.ts +28 -82
- package/src/cli/init.ts +10 -7
- package/src/cli/link.ts +20 -16
- package/src/cli/wrap.ts +14 -11
- package/src/codegen/schema-to-code.ts +2 -2
- package/src/{args.ts → core/args.ts} +32 -225
- package/src/core/commands.ts +373 -0
- package/src/core/create.ts +301 -0
- package/src/core/default-runtime.ts +239 -0
- 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 +12 -13
- package/src/extension/auto-output.ts +146 -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 +44 -0
- package/src/extension/ink.ts +93 -0
- package/src/extension/interactive.ts +106 -0
- package/src/extension/logger.ts +262 -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} +12 -12
- package/src/{interactive.ts → feature/interactive.ts} +4 -4
- package/src/{mcp.ts → feature/mcp.ts} +12 -15
- package/src/{repl-loop.ts → feature/repl-loop.ts} +10 -13
- package/src/{serve.ts → feature/serve.ts} +11 -15
- 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} +10 -8
- package/src/index.ts +115 -30
- package/src/{formatter.ts → output/formatter.ts} +124 -176
- package/src/{help.ts → output/help.ts} +22 -8
- package/src/output/output-indicator.ts +87 -0
- package/src/output/primitives.ts +335 -0
- package/src/output/styling.ts +221 -0
- package/src/{zod.d.ts → schema/zod.d.ts} +1 -1
- package/src/schema/zod.ts +50 -0
- package/src/test.ts +2 -276
- package/src/types/args-meta.ts +151 -0
- package/src/types/builder.ts +718 -0
- package/src/types/command.ts +157 -0
- package/src/types/index.ts +60 -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/{stream.ts → util/stream.ts} +27 -1
- package/src/{type-helpers.ts → util/type-helpers.ts} +23 -16
- package/src/{type-utils.ts → util/type-utils.ts} +71 -33
- package/src/util/utils.ts +51 -0
- package/src/zod.ts +1 -50
- package/dist/args-D5PNDyNu.mjs.map +0 -1
- package/dist/chunk-CjcI7cDX.mjs +0 -15
- package/dist/command-utils-B1D-HqCd.mjs +0 -1117
- package/dist/command-utils-B1D-HqCd.mjs.map +0 -1
- package/dist/completion.d.mts +0 -64
- package/dist/completion.d.mts.map +0 -1
- package/dist/completion.mjs.map +0 -1
- package/dist/errors-BiVrBgi6.mjs.map +0 -1
- package/dist/formatter-DtHzbP22.d.mts.map +0 -1
- package/dist/help-bbmu9-qd.mjs.map +0 -1
- package/dist/mcp-mLWIdUIu.mjs.map +0 -1
- package/dist/serve-B0u43DK7.mjs.map +0 -1
- package/dist/stream-BcC146Ud.mjs.map +0 -1
- package/dist/types-Ch8Mk6Qb.d.mts.map +0 -1
- package/dist/update-check-CFX1FV3v.mjs.map +0 -1
- package/src/command-utils.ts +0 -882
- package/src/create.ts +0 -1829
- package/src/runtime.ts +0 -497
- package/src/types.ts +0 -1291
- package/src/utils.ts +0 -140
- /package/src/{colorizer.ts → output/colorizer.ts} +0 -0
|
@@ -1,5 +1,20 @@
|
|
|
1
|
-
import { camelToKebab } from '
|
|
2
|
-
import {
|
|
1
|
+
import { camelToKebab } from '../util/shell-utils.ts';
|
|
2
|
+
import type { ColorConfig, ColorTheme } from './colorizer.ts';
|
|
3
|
+
import {
|
|
4
|
+
createAnsiStyler,
|
|
5
|
+
createConsoleStyler,
|
|
6
|
+
createHtmlLayout,
|
|
7
|
+
createHtmlStyler,
|
|
8
|
+
createMarkdownLayout,
|
|
9
|
+
createMarkdownStyler,
|
|
10
|
+
createTextLayout,
|
|
11
|
+
createTextStyler,
|
|
12
|
+
DEFAULT_TERMINAL_WIDTH,
|
|
13
|
+
type LayoutConfig,
|
|
14
|
+
type Styler,
|
|
15
|
+
shouldUseAnsi,
|
|
16
|
+
wrapText,
|
|
17
|
+
} from './styling.ts';
|
|
3
18
|
|
|
4
19
|
export type HelpFormat = 'text' | 'ansi' | 'console' | 'markdown' | 'html' | 'json';
|
|
5
20
|
export type HelpDetail = 'minimal' | 'standard' | 'full';
|
|
@@ -125,143 +140,6 @@ export type Formatter = {
|
|
|
125
140
|
format: (info: HelpInfo) => string;
|
|
126
141
|
};
|
|
127
142
|
|
|
128
|
-
// ============================================================================
|
|
129
|
-
// Internal Styling Types
|
|
130
|
-
// ============================================================================
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Internal styling functions used by formatters.
|
|
134
|
-
* These handle the visual styling of individual text elements.
|
|
135
|
-
*/
|
|
136
|
-
type Styler = {
|
|
137
|
-
command: (text: string) => string;
|
|
138
|
-
arg: (text: string) => string;
|
|
139
|
-
type: (text: string) => string;
|
|
140
|
-
description: (text: string) => string;
|
|
141
|
-
label: (text: string) => string;
|
|
142
|
-
section: (text: string) => string;
|
|
143
|
-
meta: (text: string) => string;
|
|
144
|
-
example: (text: string) => string;
|
|
145
|
-
exampleValue: (text: string) => string;
|
|
146
|
-
deprecated: (text: string) => string;
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Layout configuration for formatters.
|
|
151
|
-
*/
|
|
152
|
-
type LayoutConfig = {
|
|
153
|
-
newline: string;
|
|
154
|
-
indent: (level: number) => string;
|
|
155
|
-
join: (parts: string[]) => string;
|
|
156
|
-
wrapDocument?: (content: string) => string;
|
|
157
|
-
};
|
|
158
|
-
|
|
159
|
-
// ============================================================================
|
|
160
|
-
// Styler Factories
|
|
161
|
-
// ============================================================================
|
|
162
|
-
|
|
163
|
-
function createTextStyler(): Styler {
|
|
164
|
-
return {
|
|
165
|
-
command: (text) => text,
|
|
166
|
-
arg: (text) => text,
|
|
167
|
-
type: (text) => text,
|
|
168
|
-
description: (text) => text,
|
|
169
|
-
label: (text) => text,
|
|
170
|
-
section: (text) => text,
|
|
171
|
-
meta: (text) => text,
|
|
172
|
-
example: (text) => text,
|
|
173
|
-
exampleValue: (text) => text,
|
|
174
|
-
deprecated: (text) => text,
|
|
175
|
-
};
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
function createAnsiStyler(theme?: ColorTheme | ColorConfig): Styler {
|
|
179
|
-
const colorizer = createColorizer(theme);
|
|
180
|
-
return {
|
|
181
|
-
command: colorizer.command,
|
|
182
|
-
arg: colorizer.arg,
|
|
183
|
-
type: colorizer.type,
|
|
184
|
-
description: colorizer.description,
|
|
185
|
-
label: colorizer.label,
|
|
186
|
-
section: colorizer.label,
|
|
187
|
-
meta: colorizer.meta,
|
|
188
|
-
example: colorizer.example,
|
|
189
|
-
exampleValue: colorizer.exampleValue,
|
|
190
|
-
deprecated: colorizer.deprecated,
|
|
191
|
-
};
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
function createConsoleStyler(theme?: ColorTheme | ColorConfig): Styler {
|
|
195
|
-
return createAnsiStyler(theme);
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
function createMarkdownStyler(): Styler {
|
|
199
|
-
return {
|
|
200
|
-
command: (text) => `**${text}**`,
|
|
201
|
-
arg: (text) => `\`${text}\``,
|
|
202
|
-
type: (text) => `\`${text}\``,
|
|
203
|
-
description: (text) => text,
|
|
204
|
-
label: (text) => `**${text}**`,
|
|
205
|
-
section: (text) => `### ${text}`,
|
|
206
|
-
meta: (text) => `*${text}*`,
|
|
207
|
-
example: (text) => `**${text}**`,
|
|
208
|
-
exampleValue: (text) => `\`${text}\``,
|
|
209
|
-
deprecated: (text) => `~~${text}~~`,
|
|
210
|
-
};
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
function escapeHtml(text: string): string {
|
|
214
|
-
return text.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''');
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
function createHtmlStyler(): Styler {
|
|
218
|
-
return {
|
|
219
|
-
command: (text) => `<strong style="color: #00bcd4;">${escapeHtml(text)}</strong>`,
|
|
220
|
-
arg: (text) => `<code style="color: #4caf50;">${escapeHtml(text)}</code>`,
|
|
221
|
-
type: (text) => `<code style="color: #ff9800;">${escapeHtml(text)}</code>`,
|
|
222
|
-
description: (text) => `<span style="color: #666;">${escapeHtml(text)}</span>`,
|
|
223
|
-
label: (text) => `<strong>${escapeHtml(text)}</strong>`,
|
|
224
|
-
section: (text) => `<h3>${escapeHtml(text)}</h3>`,
|
|
225
|
-
meta: (text) => `<span style="color: #999;">${escapeHtml(text)}</span>`,
|
|
226
|
-
example: (text) => `<strong style="text-decoration: underline;">${escapeHtml(text)}</strong>`,
|
|
227
|
-
exampleValue: (text) => `<em>${escapeHtml(text)}</em>`,
|
|
228
|
-
deprecated: (text) => `<del style="color: #999;">${escapeHtml(text)}</del>`,
|
|
229
|
-
};
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
// ============================================================================
|
|
233
|
-
// Layout Configurations
|
|
234
|
-
// ============================================================================
|
|
235
|
-
|
|
236
|
-
function createTextLayout(): LayoutConfig {
|
|
237
|
-
return {
|
|
238
|
-
newline: '\n',
|
|
239
|
-
indent: (level) => ' '.repeat(level),
|
|
240
|
-
join: (parts) => parts.filter(Boolean).join(' '),
|
|
241
|
-
};
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
function createMarkdownLayout(): LayoutConfig {
|
|
245
|
-
return {
|
|
246
|
-
newline: '\n\n',
|
|
247
|
-
indent: (level) => {
|
|
248
|
-
if (level === 0) return '';
|
|
249
|
-
if (level === 1) return ' ';
|
|
250
|
-
return ' ';
|
|
251
|
-
},
|
|
252
|
-
join: (parts) => parts.filter(Boolean).join(' '),
|
|
253
|
-
};
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
function createHtmlLayout(): LayoutConfig {
|
|
257
|
-
return {
|
|
258
|
-
newline: '<br>',
|
|
259
|
-
indent: (level) => ' '.repeat(level),
|
|
260
|
-
join: (parts) => parts.filter(Boolean).join(' '),
|
|
261
|
-
wrapDocument: (content) => `<div style="font-family: monospace; line-height: 1.6;">${content}</div>`,
|
|
262
|
-
};
|
|
263
|
-
}
|
|
264
|
-
|
|
265
143
|
// ============================================================================
|
|
266
144
|
// Generic Formatter Implementation
|
|
267
145
|
// ============================================================================
|
|
@@ -269,7 +147,7 @@ function createHtmlLayout(): LayoutConfig {
|
|
|
269
147
|
/**
|
|
270
148
|
* Creates a formatter that uses the given styler and layout configuration.
|
|
271
149
|
*/
|
|
272
|
-
function createGenericFormatter(styler: Styler, layout: LayoutConfig, showAllBuiltins?: boolean): Formatter {
|
|
150
|
+
function createGenericFormatter(styler: Styler, layout: LayoutConfig, showAllBuiltins?: boolean, terminalWidth?: number): Formatter {
|
|
273
151
|
const { newline, indent, join, wrapDocument } = layout;
|
|
274
152
|
|
|
275
153
|
function formatUsageSection(info: HelpInfo): string[] {
|
|
@@ -366,15 +244,53 @@ function createGenericFormatter(styler: Styler, layout: LayoutConfig, showAllBui
|
|
|
366
244
|
lines.push(styler.section('Arguments:'));
|
|
367
245
|
|
|
368
246
|
const maxNameLength = Math.min(32, Math.max(...args.map((a) => a.name.length)));
|
|
247
|
+
const descCol = 2 + maxNameLength + 2;
|
|
248
|
+
const posAvailWidth = terminalWidth ? terminalWidth - descCol : undefined;
|
|
249
|
+
const descColPad = ' '.repeat(descCol);
|
|
369
250
|
|
|
370
251
|
for (const arg of args) {
|
|
371
252
|
const padding = ' '.repeat(Math.max(2, maxNameLength - arg.name.length + 2));
|
|
372
|
-
const
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
253
|
+
const prefix = indent(1) + styler.arg(arg.name) + padding;
|
|
254
|
+
|
|
255
|
+
const descPlain = arg.description ?? '';
|
|
256
|
+
const styledDesc = descPlain ? styler.description(descPlain) : '';
|
|
257
|
+
|
|
258
|
+
const metaParts: string[] = [];
|
|
259
|
+
const styledMetaParts: string[] = [];
|
|
260
|
+
if (info.usage.stdinField === arg.name) {
|
|
261
|
+
metaParts.push('(stdin)');
|
|
262
|
+
styledMetaParts.push(styler.meta('(stdin)'));
|
|
263
|
+
}
|
|
264
|
+
if (arg.enum) {
|
|
265
|
+
const text = `(choices: ${arg.enum.join(', ')})`;
|
|
266
|
+
metaParts.push(text);
|
|
267
|
+
styledMetaParts.push(styler.meta(text));
|
|
268
|
+
}
|
|
269
|
+
if (arg.default !== undefined) {
|
|
270
|
+
const text = `(default: ${String(arg.default)})`;
|
|
271
|
+
metaParts.push(text);
|
|
272
|
+
styledMetaParts.push(styler.meta(text));
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const metaStyled = join(styledMetaParts);
|
|
276
|
+
|
|
277
|
+
if (posAvailWidth && posAvailWidth > 0) {
|
|
278
|
+
const metaPlain = metaParts.join(' ');
|
|
279
|
+
const fullPlain = [descPlain, metaPlain].filter(Boolean).join(' ');
|
|
280
|
+
if (fullPlain.length <= posAvailWidth) {
|
|
281
|
+
lines.push(prefix + [styledDesc, metaStyled].filter(Boolean).join(' '));
|
|
282
|
+
} else if (!descPlain || descPlain.length <= posAvailWidth) {
|
|
283
|
+
lines.push(prefix + styledDesc);
|
|
284
|
+
if (metaStyled) lines.push(descColPad + metaStyled);
|
|
285
|
+
} else {
|
|
286
|
+
const wrapped = wrapText(descPlain, posAvailWidth);
|
|
287
|
+
lines.push(prefix + styler.description(wrapped[0]!));
|
|
288
|
+
for (const wline of wrapped.slice(1)) lines.push(descColPad + styler.description(wline));
|
|
289
|
+
if (metaStyled) lines.push(descColPad + metaStyled);
|
|
290
|
+
}
|
|
291
|
+
} else {
|
|
292
|
+
lines.push(prefix + join([styledDesc, metaStyled]));
|
|
293
|
+
}
|
|
378
294
|
}
|
|
379
295
|
|
|
380
296
|
return lines;
|
|
@@ -413,6 +329,9 @@ function createGenericFormatter(styler: Styler, layout: LayoutConfig, showAllBui
|
|
|
413
329
|
const maxNamesWidth = Math.min(32, Math.max(0, ...argColumns.map((c) => c.namesPlain.length)));
|
|
414
330
|
const maxTypeWidth = Math.min(16, Math.max(0, ...argColumns.map((c) => c.typePlain.length)));
|
|
415
331
|
const hasAnyFlags = maxFlagsWidth > 0;
|
|
332
|
+
const descCol = 2 + (hasAnyFlags ? maxFlagsWidth + 2 : 0) + maxNamesWidth + 2 + (maxTypeWidth > 0 ? maxTypeWidth + 2 : 0);
|
|
333
|
+
const argAvailWidth = terminalWidth ? terminalWidth - descCol : undefined;
|
|
334
|
+
const descColPad = ' '.repeat(descCol);
|
|
416
335
|
|
|
417
336
|
// Split into ordered groups: ungrouped first as "Options:", then each group in first-seen order
|
|
418
337
|
const grouped = Object.groupBy(argColumns, (c) => c.arg.group ?? '');
|
|
@@ -441,28 +360,65 @@ function createGenericFormatter(styler: Styler, layout: LayoutConfig, showAllBui
|
|
|
441
360
|
parts.push(styledType + typePadding);
|
|
442
361
|
}
|
|
443
362
|
|
|
444
|
-
|
|
445
|
-
const
|
|
446
|
-
if (arg.description) descParts.push(isDeprecated ? styler.deprecated(arg.description) : styler.description(arg.description));
|
|
447
|
-
lines.push(indent(1) + parts.join('') + join(descParts));
|
|
363
|
+
const prefix = indent(1) + parts.join('');
|
|
364
|
+
const contPad = argAvailWidth ? descColPad : indent(3);
|
|
448
365
|
|
|
449
|
-
//
|
|
450
|
-
const
|
|
451
|
-
|
|
452
|
-
if (
|
|
453
|
-
|
|
454
|
-
|
|
366
|
+
// Build inline meta (deprecated no-reason, default, choices)
|
|
367
|
+
const inlineMeta: string[] = [];
|
|
368
|
+
const styledInlineMeta: string[] = [];
|
|
369
|
+
if (isDeprecated && typeof arg.deprecated !== 'string') {
|
|
370
|
+
inlineMeta.push('(deprecated)');
|
|
371
|
+
styledInlineMeta.push(styler.meta('(deprecated)'));
|
|
372
|
+
}
|
|
373
|
+
if (hasDefault(arg.default)) {
|
|
374
|
+
const text = `(default: ${String(arg.default)})`;
|
|
375
|
+
inlineMeta.push(text);
|
|
376
|
+
styledInlineMeta.push(styler.meta(text));
|
|
377
|
+
}
|
|
378
|
+
if (arg.enum) {
|
|
379
|
+
const text = `(choices: ${arg.enum.join(', ')})`;
|
|
380
|
+
inlineMeta.push(text);
|
|
381
|
+
styledInlineMeta.push(styler.meta(text));
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
const descPlain = arg.description ?? '';
|
|
385
|
+
const styledDesc = descPlain ? (isDeprecated ? styler.deprecated(descPlain) : styler.description(descPlain)) : '';
|
|
386
|
+
const metaStyled = join(styledInlineMeta);
|
|
387
|
+
|
|
388
|
+
if (argAvailWidth && argAvailWidth > 0) {
|
|
389
|
+
// Terminal-width-aware: try to fit description + meta on one line
|
|
390
|
+
const metaPlain = inlineMeta.join(' ');
|
|
391
|
+
const fullPlain = [descPlain, metaPlain].filter(Boolean).join(' ');
|
|
392
|
+
if (fullPlain.length <= argAvailWidth) {
|
|
393
|
+
lines.push(prefix + [styledDesc, metaStyled].filter(Boolean).join(' '));
|
|
394
|
+
} else if (!descPlain || descPlain.length <= argAvailWidth) {
|
|
395
|
+
lines.push(prefix + styledDesc);
|
|
396
|
+
if (metaStyled) lines.push(descColPad + metaStyled);
|
|
397
|
+
} else {
|
|
398
|
+
const wrapped = wrapText(descPlain, argAvailWidth);
|
|
399
|
+
const styleFn = isDeprecated ? styler.deprecated : styler.description;
|
|
400
|
+
lines.push(prefix + styleFn(wrapped[0]!));
|
|
401
|
+
for (const wline of wrapped.slice(1)) lines.push(descColPad + styleFn(wline));
|
|
402
|
+
if (metaStyled) lines.push(descColPad + metaStyled);
|
|
403
|
+
}
|
|
404
|
+
} else {
|
|
405
|
+
// No terminal width (markdown/html): description on line 1, meta on line 2
|
|
406
|
+
const descParts: string[] = [];
|
|
407
|
+
if (styledDesc) descParts.push(styledDesc);
|
|
408
|
+
lines.push(prefix + join(descParts));
|
|
409
|
+
if (styledInlineMeta.length > 0) lines.push(indent(3) + metaStyled);
|
|
410
|
+
}
|
|
455
411
|
|
|
456
|
-
//
|
|
412
|
+
// Deprecated (with reason), examples — always on separate line
|
|
457
413
|
const line3Parts: string[] = [];
|
|
458
414
|
if (isDeprecated && typeof arg.deprecated === 'string') line3Parts.push(styler.meta(`(deprecated: ${arg.deprecated})`));
|
|
459
415
|
if (arg.examples && arg.examples.length > 0) {
|
|
460
416
|
const exampleValues = arg.examples.map((example) => (typeof example === 'string' ? example : JSON.stringify(example))).join(', ');
|
|
461
417
|
line3Parts.push(styler.example('Example:'), styler.exampleValue(exampleValues));
|
|
462
418
|
}
|
|
463
|
-
if (line3Parts.length > 0) lines.push(
|
|
419
|
+
if (line3Parts.length > 0) lines.push(contPad + join(line3Parts));
|
|
464
420
|
|
|
465
|
-
//
|
|
421
|
+
// stdin, env, config — always on separate line
|
|
466
422
|
const line4Parts: string[] = [];
|
|
467
423
|
if (info.usage.stdinField === arg.name) line4Parts.push(styler.meta('(stdin)'));
|
|
468
424
|
if (arg.env) {
|
|
@@ -472,7 +428,7 @@ function createGenericFormatter(styler: Styler, layout: LayoutConfig, showAllBui
|
|
|
472
428
|
if (arg.configKey) {
|
|
473
429
|
line4Parts.push(styler.example('Config:'), styler.exampleValue(arg.configKey));
|
|
474
430
|
}
|
|
475
|
-
if (line4Parts.length > 0) lines.push(
|
|
431
|
+
if (line4Parts.length > 0) lines.push(contPad + join(line4Parts));
|
|
476
432
|
}
|
|
477
433
|
};
|
|
478
434
|
|
|
@@ -632,18 +588,6 @@ function createJsonFormatter(): Formatter {
|
|
|
632
588
|
};
|
|
633
589
|
}
|
|
634
590
|
|
|
635
|
-
// ============================================================================
|
|
636
|
-
// Formatter Factory
|
|
637
|
-
// ============================================================================
|
|
638
|
-
|
|
639
|
-
function shouldUseAnsi(): boolean {
|
|
640
|
-
if (typeof process === 'undefined') return false;
|
|
641
|
-
if (process.env.NO_COLOR) return false;
|
|
642
|
-
if (process.env.CI) return false;
|
|
643
|
-
if (process.stdout && typeof process.stdout.isTTY === 'boolean') return process.stdout.isTTY;
|
|
644
|
-
return false;
|
|
645
|
-
}
|
|
646
|
-
|
|
647
591
|
// ============================================================================
|
|
648
592
|
// Minimal Formatter
|
|
649
593
|
// ============================================================================
|
|
@@ -673,13 +617,17 @@ export function createFormatter(
|
|
|
673
617
|
detail: HelpDetail = 'standard',
|
|
674
618
|
theme?: ColorTheme | ColorConfig,
|
|
675
619
|
all?: boolean,
|
|
620
|
+
width?: number,
|
|
621
|
+
terminal?: { columns?: number; isTTY?: boolean },
|
|
622
|
+
env?: Record<string, string | undefined>,
|
|
676
623
|
): Formatter {
|
|
677
624
|
if (detail === 'minimal') return createMinimalFormatter();
|
|
678
625
|
if (format === 'json') return createJsonFormatter();
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
626
|
+
const tw = format === 'markdown' || format === 'html' ? undefined : (width ?? terminal?.columns ?? DEFAULT_TERMINAL_WIDTH);
|
|
627
|
+
if (format === 'ansi' || (format === 'auto' && shouldUseAnsi(env, terminal?.isTTY)))
|
|
628
|
+
return createGenericFormatter(createAnsiStyler(theme), createTextLayout(), all, tw);
|
|
629
|
+
if (format === 'console') return createGenericFormatter(createConsoleStyler(theme), createTextLayout(), all, tw);
|
|
682
630
|
if (format === 'markdown') return createGenericFormatter(createMarkdownStyler(), createMarkdownLayout(), all);
|
|
683
631
|
if (format === 'html') return createGenericFormatter(createHtmlStyler(), createHtmlLayout(), all);
|
|
684
|
-
return createGenericFormatter(createTextStyler(), createTextLayout(), all);
|
|
632
|
+
return createGenericFormatter(createTextStyler(), createTextLayout(), all, tw);
|
|
685
633
|
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import type { StandardJSONSchemaV1 } from '@standard-schema/spec';
|
|
2
|
-
import { extractSchemaMetadata,
|
|
2
|
+
import { extractSchemaMetadata, getJsonSchema, type PadroneArgsSchemaMeta, parsePositionalConfig } from '../core/args.ts';
|
|
3
|
+
import { findCommandByName } from '../core/commands.ts';
|
|
4
|
+
import type { AnyPadroneCommand } from '../types/index.ts';
|
|
5
|
+
import { getRootCommand } from '../util/utils.ts';
|
|
3
6
|
import type { ColorConfig, ColorTheme } from './colorizer.ts';
|
|
4
|
-
import { findCommandByName } from './command-utils.ts';
|
|
5
7
|
import {
|
|
6
8
|
createFormatter,
|
|
7
9
|
type HelpArgumentInfo,
|
|
@@ -11,8 +13,6 @@ import {
|
|
|
11
13
|
type HelpPositionalInfo,
|
|
12
14
|
type HelpSubcommandInfo,
|
|
13
15
|
} from './formatter.ts';
|
|
14
|
-
import type { AnyPadroneCommand } from './types.ts';
|
|
15
|
-
import { getRootCommand } from './utils.ts';
|
|
16
16
|
|
|
17
17
|
export type HelpPreferences = {
|
|
18
18
|
format?: HelpFormat | 'auto';
|
|
@@ -20,6 +20,12 @@ export type HelpPreferences = {
|
|
|
20
20
|
theme?: ColorTheme | ColorConfig;
|
|
21
21
|
/** Show all global commands and flags in full detail */
|
|
22
22
|
all?: boolean;
|
|
23
|
+
/** Terminal width for text wrapping. Defaults to terminal columns or 80. */
|
|
24
|
+
width?: number;
|
|
25
|
+
/** Terminal capabilities for auto-detection of ANSI and width. */
|
|
26
|
+
terminal?: { columns?: number; isTTY?: boolean };
|
|
27
|
+
/** Environment variables for auto-detection (e.g., NO_COLOR, CI). */
|
|
28
|
+
env?: Record<string, string | undefined>;
|
|
23
29
|
};
|
|
24
30
|
|
|
25
31
|
/**
|
|
@@ -39,7 +45,7 @@ function extractPositionalArgsInfo(
|
|
|
39
45
|
const positionalConfig = parsePositionalConfig(meta.positional);
|
|
40
46
|
|
|
41
47
|
try {
|
|
42
|
-
const jsonSchema = schema
|
|
48
|
+
const jsonSchema = getJsonSchema(schema) as Record<string, any>;
|
|
43
49
|
|
|
44
50
|
if (jsonSchema.type === 'object' && jsonSchema.properties) {
|
|
45
51
|
const properties = jsonSchema.properties as Record<string, any>;
|
|
@@ -79,7 +85,7 @@ function extractArgsInfo(schema: StandardJSONSchemaV1, meta?: PadroneArgsSchemaM
|
|
|
79
85
|
const argsMeta = meta?.fields;
|
|
80
86
|
|
|
81
87
|
try {
|
|
82
|
-
const jsonSchema = schema
|
|
88
|
+
const jsonSchema = getJsonSchema(schema) as Record<string, any>;
|
|
83
89
|
|
|
84
90
|
// Handle object: z.object({ key: z.string(), ... })
|
|
85
91
|
if (jsonSchema.type === 'object' && jsonSchema.properties) {
|
|
@@ -190,7 +196,7 @@ export function getHelpInfo(cmd: AnyPadroneCommand, detail: HelpPreferences['det
|
|
|
190
196
|
hasSubcommands: !!(cmd.commands && cmd.commands.length > 0),
|
|
191
197
|
hasPositionals,
|
|
192
198
|
hasArguments: false, // updated below after extracting arguments
|
|
193
|
-
stdinField: cmd.meta?.stdin
|
|
199
|
+
stdinField: cmd.meta?.stdin,
|
|
194
200
|
},
|
|
195
201
|
};
|
|
196
202
|
|
|
@@ -366,6 +372,14 @@ export function getHelpInfo(cmd: AnyPadroneCommand, detail: HelpPreferences['det
|
|
|
366
372
|
|
|
367
373
|
export function generateHelp(rootCommand: AnyPadroneCommand, commandObj: AnyPadroneCommand = rootCommand, prefs?: HelpPreferences): string {
|
|
368
374
|
const helpInfo = getHelpInfo(commandObj, prefs?.detail, prefs?.all);
|
|
369
|
-
const formatter = createFormatter(
|
|
375
|
+
const formatter = createFormatter(
|
|
376
|
+
prefs?.format ?? 'auto',
|
|
377
|
+
prefs?.detail,
|
|
378
|
+
prefs?.theme,
|
|
379
|
+
prefs?.all,
|
|
380
|
+
prefs?.width,
|
|
381
|
+
prefs?.terminal,
|
|
382
|
+
prefs?.env,
|
|
383
|
+
);
|
|
370
384
|
return formatter.format(helpInfo);
|
|
371
385
|
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import type { KeyValueOptions, ListItem, ListOptions, TableOptions, TreeNode, TreeOptions } from './primitives.ts';
|
|
2
|
+
import { renderKeyValue, renderList, renderTable, renderTree } from './primitives.ts';
|
|
3
|
+
import type { OutputContext } from './styling.ts';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Runtime output helper injected into action context as `ctx.context.output`.
|
|
7
|
+
* Provides format-aware output primitives (table, tree, list, key-value).
|
|
8
|
+
*
|
|
9
|
+
* Each method renders data using the resolved format (ANSI, text, JSON, markdown, HTML)
|
|
10
|
+
* and writes it to the runtime's output function.
|
|
11
|
+
*/
|
|
12
|
+
export type PadroneOutputIndicator = {
|
|
13
|
+
/** Render data as a table. */
|
|
14
|
+
table(data: Record<string, unknown>[], options?: TableOptions): void;
|
|
15
|
+
/** Render data as a tree. */
|
|
16
|
+
tree(data: TreeNode | TreeNode[], options?: TreeOptions): void;
|
|
17
|
+
/** Render data as a list. */
|
|
18
|
+
list(data: ListItem[], options?: ListOptions): void;
|
|
19
|
+
/** Render data as aligned key-value pairs. */
|
|
20
|
+
kv(data: Record<string, unknown>, options?: KeyValueOptions): void;
|
|
21
|
+
/** Write raw output (same as runtime.output but sets the "already called" flag). */
|
|
22
|
+
raw(...args: unknown[]): void;
|
|
23
|
+
/** Whether any output method has been called. */
|
|
24
|
+
readonly called: boolean;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/** Declarative output configuration for a command. */
|
|
28
|
+
export type OutputPrimitiveType = 'table' | 'tree' | 'list' | 'kv' | 'json';
|
|
29
|
+
export type OutputConfig =
|
|
30
|
+
| OutputPrimitiveType
|
|
31
|
+
| { type: OutputPrimitiveType; options?: TableOptions | TreeOptions | ListOptions | KeyValueOptions };
|
|
32
|
+
|
|
33
|
+
/** Create an output indicator that renders through the given output function and format context. */
|
|
34
|
+
export function createOutputIndicator(outputFn: (...args: unknown[]) => void, ctx: OutputContext): PadroneOutputIndicator {
|
|
35
|
+
let _called = false;
|
|
36
|
+
|
|
37
|
+
const emit = (rendered: string) => {
|
|
38
|
+
_called = true;
|
|
39
|
+
outputFn(rendered);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
table(data, options) {
|
|
44
|
+
emit(renderTable(data, options, ctx));
|
|
45
|
+
},
|
|
46
|
+
tree(data, options) {
|
|
47
|
+
emit(renderTree(data, options, ctx));
|
|
48
|
+
},
|
|
49
|
+
list(data, options) {
|
|
50
|
+
emit(renderList(data, options, ctx));
|
|
51
|
+
},
|
|
52
|
+
kv(data, options) {
|
|
53
|
+
emit(renderKeyValue(data, options, ctx));
|
|
54
|
+
},
|
|
55
|
+
raw(...args) {
|
|
56
|
+
_called = true;
|
|
57
|
+
outputFn(...args);
|
|
58
|
+
},
|
|
59
|
+
get called() {
|
|
60
|
+
return _called;
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Format a return value using a declarative output config. */
|
|
66
|
+
export function formatDeclarativeOutput(value: unknown, config: OutputConfig, ctx: OutputContext): string | undefined {
|
|
67
|
+
const type = typeof config === 'string' ? config : config.type;
|
|
68
|
+
const options = typeof config === 'object' ? config.options : undefined;
|
|
69
|
+
|
|
70
|
+
switch (type) {
|
|
71
|
+
case 'table':
|
|
72
|
+
if (!Array.isArray(value)) return undefined;
|
|
73
|
+
return renderTable(value as Record<string, unknown>[], options as TableOptions | undefined, ctx);
|
|
74
|
+
case 'tree':
|
|
75
|
+
return renderTree(value as TreeNode | TreeNode[], options as TreeOptions | undefined, ctx);
|
|
76
|
+
case 'list':
|
|
77
|
+
if (!Array.isArray(value)) return undefined;
|
|
78
|
+
return renderList(value as ListItem[], options as ListOptions | undefined, ctx);
|
|
79
|
+
case 'kv':
|
|
80
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) return undefined;
|
|
81
|
+
return renderKeyValue(value as Record<string, unknown>, options as KeyValueOptions | undefined, ctx);
|
|
82
|
+
case 'json':
|
|
83
|
+
return JSON.stringify(value, null, 2);
|
|
84
|
+
default:
|
|
85
|
+
return undefined;
|
|
86
|
+
}
|
|
87
|
+
}
|