Arguments Metadata
Padrone uses Zod schemas with .meta() to configure CLI-specific behavior. This reference covers all available metadata configuration.
Zod Meta Configuration
Section titled “Zod Meta Configuration”Use .meta() on individual Zod schema properties to configure their CLI behavior:
z.object({ output: z.string() .describe('Output file path') .meta({ flags: 'o', alias: 'out', examples: ['output.json', './dist/bundle.js'], }),})Available Meta Properties
Section titled “Available Meta Properties”| Property | Type | Description |
|---|---|---|
flags | string | string[] | Single-character short flags (e.g., 'p' for -p). Stackable: -abc = -a -b -c |
alias | string | string[] | Multi-character long aliases (e.g., 'dry-run' for --dry-run) |
negative | string | string[] | Custom negative keyword(s) for booleans. Disables --no- prefix |
examples | unknown[] | Example values shown in help |
deprecated | string | boolean | Mark as deprecated with optional message |
hidden | boolean | Hide from help output |
group | string | Group name for organizing under a labeled section in help output |
Meta Configuration
Section titled “Meta Configuration”The second argument to .arguments() configures positional arguments and per-argument metadata:
.arguments(schema, { positional: ['source', '...files', 'dest'], fields: { verbose: { flags: 'v' }, dryRun: { alias: 'dry' }, format: { deprecated: 'Use --output instead' }, },})positional
Section titled “positional”Array of argument names to accept as positional arguments.
{ positional: ['source', 'dest'] }Positional argument order:
- Arguments are matched in the order specified
- Optional arguments are skipped if not provided
- Position matters:
['source', 'dest']means first arg is source, second is dest
Variadic arguments:
- Prefix with
...to capture multiple values:['...files'] - Variadic args must be arrays in the schema:
z.array(z.string()) - Only one variadic argument is allowed per command
- Variadic can be at any position
// Capture all args between fixed positions{ positional: ['command', '...args', 'output'] }// command = first arg// args = all middle args// output = last argfields
Section titled “fields”Per-argument configuration that supplements or overrides .meta():
{ fields: { verbose: { flags: 'v' }, dryRun: { alias: 'dry' }, format: { deprecated: 'Use --output instead', hidden: true, }, },}This is equivalent to using .meta() on the schema property but allows configuration to be kept separate from the schema definition. Fields accept the same properties as Zod .meta(): flags, alias, negative, description, examples, deprecated, hidden, group.
autoAlias
Section titled “autoAlias”Automatically generate kebab-case aliases for camelCase argument names. Enabled by default.
// Default (autoAlias: true): --dry-run automatically maps to dryRun.arguments(z.object({ dryRun: z.boolean() }))
// Disable auto-aliases.arguments(z.object({ dryRun: z.boolean() }), { autoAlias: false })Read from stdin and inject the data into a specified argument field. Only reads when stdin is piped (not a TTY) and the field wasn’t already provided via CLI flags. The read mode is inferred from the schema: string fields read all stdin as text, string[] fields read line-by-line.
// Read all stdin as text into 'data' field.arguments(z.object({ data: z.string() }), { stdin: 'data' })
// Read stdin line-by-line into an array field (inferred from array schema).arguments( z.object({ lines: z.array(z.string()) }), { stdin: 'lines' })
// Stream stdin lazily as AsyncIterable (for large inputs)import { zodAsyncStream } from 'padrone/zod';.arguments( z.object({ lines: zodAsyncStream() }), { stdin: 'lines' })
// Typed stream with JSON codec (each line JSON.parse'd and validated)import { zodAsyncStream, jsonCodec } from 'padrone/zod';const itemSchema = z.object({ name: z.string(), age: z.number() });.arguments( z.object({ records: zodAsyncStream(jsonCodec(itemSchema)) }), { stdin: 'records' })interactive
Section titled “interactive”Declare which fields should be interactively prompted when their values are missing after CLI/env/config resolution. Only takes effect in cli() and eval() when the runtime has interactive: true.
// Prompt all missing required fields{ interactive: true }
// Prompt specific fields{ interactive: ['name', 'template'] }When interactive is set, parse() and cli() return Promises (the command becomes async).
Prompt types are auto-detected from the schema:
| Schema Type | Prompt |
|---|---|
z.boolean() | Confirm (yes/no) |
z.enum([...]) | Select (single choice) |
z.array(z.enum([...])) | Multi-select |
z.string() | Text input |
| Any other type | Text input |
The prompt message is derived from the field’s .describe() text, or from fields meta description, falling back to the field name.
optionalInteractive
Section titled “optionalInteractive”Additional fields offered after required interactive prompts. Users are shown a multi-select to choose which of these fields to configure.
// Offer all missing optional fields{ optionalInteractive: true }
// Offer specific fields{ optionalInteractive: ['verbose', 'format'] }Example combining both:
.arguments( z.object({ name: z.string().describe('Project name'), template: z.enum(['react', 'vue', 'svelte']).describe('Starter template'), typescript: z.boolean().default(false).describe('Use TypeScript'), eslint: z.boolean().default(false).describe('Add ESLint'), }), { positional: ['name'], interactive: ['name', 'template'], optionalInteractive: ['typescript', 'eslint'], })When running without arguments, this will:
- Prompt for
nameandtemplate(required interactive fields) - Show a multi-select: “Would you also like to configure: TypeScript, ESLint”
- Prompt individually for any selected optional fields
See the Interactive Prompting guide for full details.
Environment Variables
Section titled “Environment Variables”Bind arguments to environment variables using the padroneEnv extension:
import { createPadrone, padroneEnv } from 'padrone';import * as z from 'zod/v4';
const program = createPadrone('app') .command('serve', (c) => c .arguments( z.object({ port: z.number().default(3000), apiKey: z.string(), }), ) .extend( padroneEnv( z.object({ APP_PORT: z.coerce.number().optional(), API_KEY: z.string().optional(), }).transform((env) => ({ port: env.APP_PORT, apiKey: env.API_KEY, })) ) ) .action((args) => { console.log(`Server on port ${args.port}`); }), );The env schema validates process.env and transforms env var names into argument names. Only provided env values are used — undefined values are skipped. padroneEnv can be applied at the program level (inherited by all commands) or at the command level.
Resolution priority:
- CLI argument (highest)
- Stdin
- Environment variable
- Config file
- Interactive prompt (if runtime supports it)
- Default value (lowest)
Config Files
Section titled “Config Files”Load arguments from configuration files using the padroneConfig extension:
import { createPadrone, padroneConfig } from 'padrone';import * as z from 'zod/v4';
const program = createPadrone('app') .command('serve', (c) => c .arguments( z.object({ port: z.number().default(3000), host: z.string().default('localhost'), }), ) .extend( padroneConfig({ files: 'app.config.json', schema: z.object({ port: z.number().optional(), host: z.string().optional(), }), }) ) .action((args) => { console.log(`Server on ${args.host}:${args.port}`); }), );Multiple config file paths can be provided in the files array — the first existing file is used:
.extend(padroneConfig({ files: ['app.config.json', '.apprc'] }))If no schema is provided, the config file values are matched against the command’s argument schema directly.
Short argument flags allow single-character shortcuts:
z.object({ verbose: z.boolean().optional().meta({ flags: 'v' }), port: z.number().default(3000).meta({ flags: 'p' }), output: z.string().optional().meta({ flags: 'o' }),})Usage:
app -v -p 8080 -o output.json# Equivalent to:app --verbose --port 8080 --output output.jsonCombined short flags:
app -vp 8080# -v (boolean) + -p 8080Aliases
Section titled “Aliases”Multi-character long aliases provide alternative names for arguments:
z.object({ dryRun: z.boolean().optional().meta({ alias: 'dry' }),})app --dry# Equivalent to:app --dry-run # (auto-alias from camelCase)app --dryRun # (original name)Custom Negation
Section titled “Custom Negation”By default, boolean arguments can be negated with the --no- prefix (e.g., --no-verbose). The negative meta option lets you define custom keyword(s) that set a boolean to false, while disabling the default --no- prefix:
z.object({ local: z.boolean().default(true).meta({ negative: 'remote' }),})app --remote# Equivalent to: local = false# --no-local is NOT recognized (disabled by custom negation)
app --local# local = true (still works normally)Multiple negative keywords:
z.object({ local: z.boolean().default(true).meta({ negative: ['remote', 'cloud'] }),})app --remote # local = falseapp --cloud # local = falseDisable --no- prefix only:
Set negative to an empty string or empty array to disable the --no- prefix without adding any alternative keywords:
z.object({ verbose: z.boolean().default(true).meta({ negative: '' }),})app --verbose # verbose = trueapp --no-verbose # Error: unknown optionCustom negation can also be set via fields meta:
.arguments(z.object({ local: z.boolean().default(true) }), { fields: { local: { negative: 'remote' } },})The stringify() method uses the first negative keyword when serializing false values:
program.stringify('cmd', { local: false })// → "cmd --remote" (instead of "cmd --no-local")Deprecation
Section titled “Deprecation”Mark arguments as deprecated to warn users:
z.object({ // Simple deprecation old: z.string().optional().meta({ deprecated: true }),
// With migration message legacy: z.string().optional().meta({ deprecated: 'Use --new-arg instead', }),})Deprecated arguments still work but display a warning when used.
Hidden Arguments
Section titled “Hidden Arguments”Hide arguments from help output while keeping them functional:
z.object({ // Visible in help port: z.number().default(3000),
// Hidden from help debug: z.boolean().optional().meta({ hidden: true }), internalFlag: z.string().optional().meta({ hidden: true }),})Hidden arguments:
- Don’t appear in
--helpoutput - Still work when specified
- Useful for internal/experimental features
Examples in Help
Section titled “Examples in Help”Provide example values for help text:
z.object({ format: z.enum(['json', 'csv', 'xml']) .describe('Output format') .meta({ examples: ['json', 'csv'] }),
date: z.string() .describe('Date filter') .meta({ examples: ['2024-01-01', 'today', 'last-week'] }),})Examples appear in the generated help text to guide users.
Groups
Section titled “Groups”Organize arguments into labeled sections in help output:
z.object({ port: z.number().default(3000).meta({ group: 'Server' }), host: z.string().default('localhost').meta({ group: 'Server' }), verbose: z.boolean().optional().meta({ group: 'Debug' }), logLevel: z.enum(['info', 'debug', 'warn']).optional().meta({ group: 'Debug' }),})Arguments with the same group name are displayed together under a labeled section in help output.