Skip to content

API Reference

Creates a new Padrone program with the given name.

import { createPadrone } from 'padrone';
const program = createPadrone('myapp');

Parameters:

  • name (string): The program name, used in help output and as the root command name

Returns: A PadroneProgram builder instance


Configure program or command properties.

program.configure({
title: 'My Application',
description: 'A helpful CLI tool',
version: '1.0.0',
});

Configuration:

PropertyTypeDescription
titlestringDisplay title for help output
descriptionstringProgram/command description
versionstringVersion string
deprecatedboolean | stringMark as deprecated with optional message
hiddenbooleanHide from help output
groupstringGroup name for organizing in help output
mutationbooleanMark as mutation (POST-only in serve, destructiveHint in MCP, defaults needsApproval in tool)

Configure the runtime adapter for I/O abstraction. Allows the CLI framework to work outside of a terminal (e.g., web UIs, chat interfaces, AI agents, testing).

program.runtime({
interactive: true,
prompt: myCustomPromptFn,
output: (text) => panel.append(text),
error: (text) => panel.appendError(text),
format: 'html',
});

Configuration:

PropertyTypeDefaultDescription
output(text: string) => voidconsole.logWrite normal output
error(text: string) => voidconsole.errorWrite error output
argv() => string[]process.argv.slice(2)Return raw CLI arguments
env() => Record<string, string | undefined>process.envReturn environment variables
formatstring'auto'Default help output format
interactivebooleanfalseWhether the runtime supports interactive prompts
prompt(config: InteractivePromptConfig) => Promise<unknown>Enquirer (when interactive: true)Custom prompt implementation
progress(message: string, options?: PadroneProgressOptions) => PadroneProgressIndicatorBuilt-in terminal spinnerProgress indicator factory. See Progress Indicators

Successive .runtime() calls merge with previous configuration.


Define arguments using a Zod schema.

program.arguments(
z.object({
port: z.number().default(3000).describe('Port number'),
host: z.string().default('localhost'),
}),
{
positional: ['port'],
fields: {
host: { flags: 'h' },
},
}
);

Parameters:

  • schema: Zod object schema defining the arguments
  • meta (optional): Additional configuration
    • positional: Array of argument names to treat as positional arguments
    • fields: Per-argument metadata (flags, alias, description, examples, deprecated, hidden, group)
    • interactive: true | string[] — fields to prompt when missing (see Interactive Prompting)
    • optionalInteractive: true | string[] — optional fields offered after required prompts
    • autoAlias: boolean — auto-generate kebab-case aliases for camelCase names (default: true)
    • stdin: string | { field, as } — read from stdin into an argument field

When interactive or optionalInteractive is set, the command becomes async — parse() and cli() return Promises.


Set or transform the typed context for this command. Context is a user-defined object that flows through the command tree. Subcommands inherit the parent’s context type but can transform it.

// Type-only — declare context type without runtime transform
program.context<{ db: Database }>();
// With transform — modify inherited context at runtime
program.context((parentCtx) => ({ ...parentCtx, logger: createLogger() }));
// Chainable — multiple calls compose transforms
program
.context<{ db: Database }>()
.context((ctx) => ({ ...ctx, logger: createLogger() }));

When called without arguments, .context<T>() only changes the TypeScript type. When called with a transform function, the function is applied at runtime to the inherited context as it resolves from root to the target command.

Returns: The program builder (chainable)


Set the handler function for the command.

program.action((args, ctx) => {
console.log('Arguments:', args);
console.log('Context:', ctx.context);
return { success: true };
});

Parameters:

  • handler: Function receiving (args, ctx, base)
    • args: Parsed and validated arguments object
    • ctx: Action context object containing runtime, command, program, progress, and context (see below)
    • base: Previous handler function (useful when overriding commands)

Action Context (ctx):

PropertyTypeDescription
runtimeResolvedPadroneRuntimeThe resolved runtime for this command
commandPadroneCommandThe command being executed
programPadroneProgramThe root program instance
progressPadroneProgressIndicatorAuto-managed progress indicator, or lazy indicator for manual use. See Progress Indicators
contextTContextUser-defined context, resolved through the command parent chain

Returns: The program builder (chainable)


Extension for parsing environment variables into arguments. The schema validates process.env and transforms env var names into argument field names. Imported from 'padrone'.

import { createPadrone, padroneEnv } from 'padrone';
program.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,
}))
)
);

Parameters:

  • schema: A Standard Schema that validates env vars and transforms them to argument names

Env values are applied after CLI args and stdin, but before config file values. Can be applied at the program level (inherited by all commands) or at the command level.


Extension for loading arguments from configuration files. Not included by default — must be explicitly applied via .extend(padroneConfig(...)). Imported from 'padrone'.

import { createPadrone, padroneConfig } from 'padrone';
// Simple: config file with matching argument names
program.extend(padroneConfig({ files: 'app.config.json' }));
// With schema: transform config keys to argument names
program.extend(
padroneConfig({
files: 'app.config.json',
schema: z.object({
port: z.number().optional(),
apiKey: z.string().optional(),
}),
})
);
// Multiple file paths (first found wins)
program.extend(padroneConfig({ files: ['app.config.json', '.apprc'] }));
// Disable config loading
program.extend(padroneConfig({ files: 'app.config.json', disabled: true }));

Options:

PropertyTypeDescription
filesstring | string[]Config file path(s). When multiple paths are provided, the first existing file is used
schemaStandardSchemaOptional schema to validate/transform config values
disabledbooleanDisable config file loading
flagbooleanEnable/disable the --config/-c flag (default: true)
inheritbooleanWhether the config interceptor inherits to subcommands (default: true)
loadConfig(files: string | string[]) => Record<string, unknown> | undefined | Promise<...>Custom config loader function. Replaces the built-in JSON/YAML/TOML loader

Config values have the lowest precedence: CLI > stdin > env > config. Not included by default — must be explicitly applied via .extend(padroneConfig(...)). Can be applied at the program level (inherited by all commands) or at the command level.


Experimental: This API is experimental and may change in future releases.

Wrap an external CLI tool with optional schema transformation in the config object.

The config can include a schema property that transforms command arguments to external CLI arguments. The schema’s input type should match the current command’s arguments (from .arguments()), and its output type defines the arguments expected by the external command.

// Define command arguments first
program
.command('commit', (c) =>
c
.arguments(
z.object({
message: z.string(),
all: z.boolean().optional(),
}),
{
positional: ['message'],
}
)
.wrap({
command: 'git',
args: ['commit'],
positional: ['m'], // Positional for external command
schema: z.object({
message: z.string(),
all: z.boolean().optional(),
}).transform((args) => ({
m: args.message, // Map 'message' to 'm' flag
a: args.all, // Map 'all' to 'a' flag
})),
})
);

Configuration:

PropertyTypeDefaultDescription
commandstringrequiredThe external command to execute (e.g., ‘git’, ‘docker’, ‘npm’)
argsstring[][]Fixed arguments that always precede the arguments (e.g., ['commit'] for ‘git commit’)
positionalstring[]command’s positionalPositional argument configuration for the external command. Defaults to the wrapping command’s positional config.
inheritStdiobooleantrueWhether to inherit stdio streams from the parent process. Set to false to capture stdout/stderr
schemaSchema | (cmdSchema) => SchemaidentityOptional transformation schema. If not provided, command arguments are passed through as-is.

Returns: The program builder with an action that executes the external command

Result Type: WrapResult (when inheritStdio is false)

type WrapResult = {
exitCode: number; // The exit code of the process
stdout?: string; // Standard output (only if inheritStdio is false)
stderr?: string; // Standard error (only if inheritStdio is false)
success: boolean; // Whether the process exited successfully (exit code 0)
}

Examples:

// No transformation - pass arguments through as-is
program
.command('echo', (c) =>
c
.arguments(z.object({ message: z.string() }))
.wrap({
command: 'echo',
})
);
// Function-based schema for type safety
program
.command('run', (c) =>
c
.arguments(
z.object({
image: z.string(),
detach: z.boolean().optional(),
interactive: z.boolean().optional(),
})
)
.wrap({
command: 'docker',
args: ['run'],
positional: ['image'],
schema: (cmdSchema) => cmdSchema.transform(args => ({
image: args.image,
d: args.detach,
i: args.interactive,
})),
})
);
// Usage: program.run('run', { image: 'nginx', detach: true })
// After transform: { image: 'nginx', d: true }
// Executes: docker run --d nginx
// Direct schema with transform
program
.command('install', (c) =>
c
.arguments(
z.object({
packages: z.string().array(),
saveDev: z.boolean().optional(),
}),
{
positional: ['...packages'],
}
)
.wrap({
command: 'npm',
args: ['install'],
positional: ['...packages'],
inheritStdio: false, // Capture output
schema: z.object({
packages: z.string().array(),
saveDev: z.boolean().optional(),
}).transform(args => ({
packages: args.packages,
'save-dev': args.saveDev, // Map to exact flag name
})),
})
);
// Usage:
const result = await program.run('install', {
packages: ['react', 'react-dom'],
saveDev: true,
});
console.log(result.result.stdout); // npm install output
console.log(result.result.exitCode); // 0 if successful

Type Safety:

The .wrap() method maintains full type safety:

  • Input schema matches command arguments from .arguments()
  • Output schema defines external CLI arguments structure
  • Return type is inferred as Promise<WrapResult>
  • TypeScript enforces correct types when calling .run() or .cli()

How it works:

  1. Schema Transformation: The wrap schema transforms command arguments to external CLI arguments

    • Input: Parsed command arguments (from .arguments())
    • Output: External program arguments
  2. Arguments → CLI Arguments: Padrone converts transformed arguments to CLI arguments:

    • Boolean arguments: { verbose: true }--verbose
    • String/Number arguments: { port: 3000 }--port 3000
    • Array arguments: { files: ['a', 'b'] }--files a --files b
    • Positional arguments: Follow the order specified in config.positional
    • Argument keys are used as-is with -- prefix
  3. Process Execution: Uses spawn to execute the external command with the generated arguments


Configure an auto-managed progress indicator for the command. The indicator starts before validation and is automatically stopped on success or failure. See the Progress Indicators guide for full details.

// Simple message
.progress('Deploying...')
// Full config
.progress({
message: {
validation: 'Validating...',
progress: 'Deploying...',
success: (result) => `Deployed v${result.version}`,
error: 'Deploy failed',
},
spinner: 'line',
bar: true,
time: true,
eta: true,
})
// Dynamic indicator icons
.progress({
message: {
progress: 'Running...',
success: (result) => ({ message: 'All passed', indicator: '🎉' }),
},
})

Parameters:

  • config (optional): string | PadroneProgressConfig
    • string — custom progress message for all states
    • Object — full control with messages, visual options, and renderer

Object fields:

PropertyTypeDescription
messagestring | PadroneProgressMessagesPer-phase messages. String sets the progress message. Object has validation, progress, success, error
spinnerPadroneSpinnerConfigSpinner preset, true (always show), false (disable), or { frames, interval, show }
barboolean | PadroneBarConfigtrue for defaults, or { width, filled, empty, animation, show } for customization
timebooleanShow elapsed time (⏱ M:SS). Can also be toggled via update({ time: true/false })
etabooleanShow estimated time remaining (ETA M:SS). Requires numeric update() calls
rendererPadroneProgressRendererCustom renderer factory (defaults to built-in terminal renderer)

PadroneProgressMessages fields: validation (string), progress (string), success (string/null/callback), error (string/null/callback). Callbacks can return a string, null (suppress), or { message, indicator } for per-call icon customization. Messages can also be provided from context via progressConfig.message — command-level fields take precedence.

Returns: The program builder (chainable)


Register an interceptor for middleware-style interception of command phases. See the Interceptors & Extensions guide for full details.

import { defineInterceptor } from 'padrone';
const logger = defineInterceptor({ name: 'logger' }, () => ({
execute: (ctx, next) => {
console.log(`Running: ${ctx.command.name}`);
const result = next();
console.log(`Done: ${ctx.command.name}`);
return result;
},
}));
program.intercept(logger);

Parameters:

  • interceptor: A PadroneInterceptor — either created with defineInterceptor() or a plain object with name, optional order/id/disabled, and phase handlers (start, parse, validate, execute, error, shutdown)

Returns: New builder with the interceptor added (immutable)

Available on both programs and subcommand builders. Program-level interceptors apply as outermost wrappers; subcommand interceptors compose as inner layers.


Apply a build-time extension. An extension is a function that receives the builder and returns a modified builder. Extensions can add commands, arguments, interceptors, and configuration.

import { createPadrone, padroneEnv, padroneConfig, padroneProgress } from 'padrone';
const program = createPadrone('myapp')
.extend(padroneEnv(envSchema))
.extend(padroneConfig({ files: 'config.json' }))
.command('deploy', (c) =>
c
.extend(padroneProgress('Deploying...'))
.action(async () => { /* ... */ })
);

Parameters:

  • extension: A function (builder) => builder — receives the current builder and returns a modified builder

Returns: The modified builder returned by the extension

Extensions compose naturally — chain multiple .extend() calls to layer functionality. Most of Padrone’s built-in features are implemented as extensions applied automatically by createPadrone(). See the Interceptors & Extensions guide for the full list.


Enable background version checking against a package registry. When enabled, the program checks for a newer version in the background and displays a notification after command output.

program.updateCheck({
registry: 'npm', // or custom URL
interval: '1d', // check at most once per day
cache: '~/.myapp-update',
});

Configuration:

PropertyTypeDefaultDescription
packageNamestringauto-detectedPackage name to check
registrystring'npm'Registry URL or 'npm' shorthand
intervalstring'1d'Check interval (e.g., '1d', '12h', '30m', '1w')
cachestringautoPath to cache file for last check timestamp
disableEnvVarstringautoEnv var name that disables update checking

Non-blocking, respects CI environments, and caches check timestamps.

Returns: The program builder (chainable)


Explicitly mark a command as using async validation. When async, parse() and cli() return Promises.

program.command('create', (c) =>
c
.arguments(z.object({ name: z.string() }).check(async (ctx) => { /* ... */ }))
.async()
.action((args) => args.name)
);

This is an alternative to asyncSchema() when you can’t brand the schema itself.

Returns: The program builder (chainable)


Add a subcommand. Re-registering a command with the same name merges the definitions — see Program Composition for details.

program.command('serve', (c) =>
c
.arguments(schema)
.action(handler)
);
// With aliases
program.command(['serve', 's'], (c) =>
c.arguments(schema).action(handler)
);

Parameters:

  • name: Command name string, or [name, ...aliases] array for aliases
  • builder: Function receiving a command builder, returns configured command

Execute the program as a CLI. This is the main process entry point that reads from process.argv and throws on validation errors.

// Parse process.argv
program.cli();
// With preferences
program.cli({ interactive: true });
// With context
program.cli({ context: { db, logger } });
// Start a REPL session from CLI
// myapp --repl
// myapp --repl db

Parameters:

  • prefs (optional): PadroneCliPreferences
    • interactive: Override interactive prompting (true = force, false = suppress, undefined = inherit from runtime)
    • runtime: Override runtime configuration
    • context: User-defined context object. Required when the program has a non-unknown context type.

Returns: PadroneCommandResult with command, args, argsResult, and result. Returns a Promise when the matched command is async.

Note: Interactive prompting only triggers in cli() and eval(), not in parse() or run(). When a command has interactive meta and the runtime has interactive: true, missing field values are prompted before validation. The --repl flag starts a REPL session (optionally scoped to a command).


Parse, validate, and execute a command string with soft error handling. Returns a result with issues instead of throwing on validation errors.

const result = await program.eval('serve --port 8080');
const result = await program.eval('serve --port 8080', { context: { db } });
if (result.argsResult?.issues) {
console.error('Validation errors:', result.argsResult.issues);
} else {
console.log('Result:', result.result);
}

Parameters:

  • input: Command string to parse and execute
  • preferences (optional): { interactive?: boolean, context?: TContext } — override interactive prompting and provide context

Returns: PadroneCommandResult with command, args, argsResult, and result. Returns a Promise when the matched command is async.

Difference from cli(): eval() is designed for programmatic use (REPL, AI tools, testing). It uses soft error handling — validation failures are returned as argsResult.issues rather than thrown. cli() is the process entry point that throws on errors and reads from process.argv.


Run a command programmatically with typed arguments.

const result = program.run('serve', {
port: 8080,
host: 'localhost',
});
// With context
const result = program.run('serve', { port: 8080 }, { context: { db } });

Parameters:

  • command: Command path (e.g., 'serve' or 'db migrate up')
  • args: Arguments object matching the command’s schema
  • prefs (optional): { context?: TContext } — provide context

Returns: The action handler’s return value


Parse input without executing the action.

const result = program.parse('serve --port 8080');
console.log(result.command); // PadroneCommand for 'serve'
console.log(result.args); // { port: 8080, host: 'localhost' }
console.log(result.argsResult); // Standard Schema validation result

Parameters:

  • input (optional): String or string array to parse

Returns: PadroneParseResult with command, args, and argsResult properties. Returns a Promise when the matched command is async.


Convert a command and arguments back to a CLI string.

const cliString = program.stringify('serve', { port: 8080 });
// 'serve --port 8080'

Parameters:

  • command (optional): Command name
  • args (optional): Arguments object

Returns: CLI string representation


Generate a typed API object for programmatic use.

const api = program.api();
// Call commands as methods
api.serve({ port: 8080 });
api.db.migrate.up({ steps: 1 });

Returns: Typed API object with methods for each command


Generate help text.

// Program help
console.log(program.help());
// Command help
console.log(program.help('serve'));
// Different formats
program.help('', { format: 'markdown' });

Parameters:

  • command (optional): Command to get help for
  • preferences (optional): { format: 'text' | 'ansi' | 'console' | 'markdown' | 'html' | 'json', detail: 'minimal' | 'standard' | 'full', theme: ColorTheme | ColorConfig }

Returns: Help text string (or object for JSON format)


Experimental: This API is experimental and may change in future releases.

Start a Model Context Protocol server, exposing all commands as MCP tools.

// Start with defaults (HTTP on port 3000)
await program.mcp();
// With options
await program.mcp({
transport: 'stdio',
port: 8080,
host: '0.0.0.0',
cors: 'https://example.com',
});

Parameters:

  • prefs (optional): PadroneMcpPreferences
PropertyTypeDefaultDescription
transport'http' | 'stdio''http'Transport mode
portnumber3000HTTP port
hoststring'127.0.0.1'HTTP host
basePathstring'/mcp'HTTP endpoint path
namestringprogram nameServer name reported to clients
versionstringprogram versionServer version reported to clients
corsstring | false'*'CORS allowed origin, or false to disable

Returns: Promise<void> (resolves when the server shuts down)

The HTTP transport implements the 2025-11-25 Streamable HTTP spec with session management, SSE support (via Accept: text/event-stream), and CORS headers.

Also available as a built-in CLI command: myapp mcp [http|stdio] --port 3000 --host 0.0.0.0


Experimental: This API is experimental and may change in future releases.

Start a REST HTTP server that exposes commands as endpoints. Each command becomes a route (users list/users/list). Commands with mutation: true only accept POST; others accept both GET and POST.

// Start with defaults (port 3000)
await program.serve();
// With options
await program.serve({
port: 8080,
host: '0.0.0.0',
basePath: '/api/',
cors: 'https://example.com',
});

Parameters:

  • prefs (optional): PadroneServePreferences
PropertyTypeDefaultDescription
portnumber3000HTTP port
hoststring'127.0.0.1'HTTP host
basePathstring'/'Base path prefix for all routes
corsstring | false'*'CORS allowed origin, or false to disable
builtins.healthbooleantrueEnable GET /_health endpoint
builtins.helpbooleantrueEnable GET /_help and GET /_help/:command
builtins.schemabooleantrueEnable GET /_schema and GET /_schema/:command
builtins.docsbooleantrueEnable GET /_docs (Scalar OpenAPI viewer) and GET /_openapi
onRequest(req: Request) => Response | void | Promise<...>Hook to run before each request (auth, rate-limiting)
onError(error: unknown, req: Request) => ResponseCustom error response handler

Returns: Promise<void> (resolves when the server shuts down)

Request handling:

  • GET requests: Query parameters are converted to CLI flags → eval()
  • POST requests: JSON body is serialized to CLI flags → eval()
  • Mutation commands: Only accept POST (returns 405 for GET)

Built-in endpoints:

EndpointDescription
GET /_healthReturns { status: "ok" }
GET /_helpProgram help (JSON or markdown based on Accept header)
GET /_help/:pathCommand-specific help
GET /_schemaJSON Schema map of all commands
GET /_schema/:pathJSON Schema for a single command
GET /_docsScalar OpenAPI docs viewer
GET /_openapiRaw OpenAPI 3.1.0 JSON spec

Also available as a built-in CLI command: myapp serve --port 3000 --host 0.0.0.0 --base-path /api/


Generate a Vercel AI SDK compatible tool.

import { streamText } from 'ai';
const tool = program.tool();
await streamText({
model: yourModel,
tools: { myapp: tool },
});

Returns: AI SDK tool object


Mount an existing Padrone program as a subcommand. All nested commands are recursively re-pathed. See the Program Composition guide for full details.

const users = createPadrone('users')
.command('list', (c) => c.action(() => 'listing users'))
.command('create', (c) =>
c.arguments(z.object({ name: z.string() })).action((args) => args.name)
);
const app = createPadrone('app')
.mount('users', users);
// With aliases
const app2 = createPadrone('app')
.mount(['users', 'u'], users);
// With context transform
const app3 = createPadrone('app')
.context<{ db: Database }>()
.mount('users', users, {
context: (appCtx) => ({ db: appCtx.db }),
});

Parameters:

  • name: Command name string, or [name, ...aliases] array for aliases
  • program: A Padrone program to mount
  • options (optional): { context?: (parentCtx) => mountedCtx } — transform the parent’s context into what the mounted program expects

Returns: New builder with the mounted program


Start an interactive REPL session. Returns an AsyncIterable that yields a result for each executed command. See the REPL guide for full details.

for await (const result of program.repl()) {
console.log(result.command.name, result.result);
}
// With options
for await (const result of program.repl({
prompt: 'app> ',
scope: 'db',
greeting: 'Welcome!',
})) {
// ...
}

Parameters:

  • options (optional): REPL preferences
    • prompt: Custom prompt string or function
    • greeting: Welcome message (false to suppress)
    • hint: Hint text below greeting (false to suppress)
    • history: Initial history entries
    • completion: Enable tab completion (default: true)
    • spacing: Output separators (before/after command output)
    • outputPrefix: Prefix for output lines
    • scope: Start scoped to a command path (strongly typed)

Returns: AsyncIterable<PadroneCommandResult>


Find a command by name.

const cmd = program.find('db migrate up');
if (cmd) {
console.log(cmd.name); // 'up'
}

Parameters:

  • command: Command path string

Returns: Command instance or undefined


Generate shell completion script.

const script = program.completion('bash');
// Or: 'zsh', 'fish', 'powershell'

Parameters:

  • shell (optional): Target shell. Auto-detected if omitted.

Returns: Shell completion script string


Padrone supports color themes for ANSI help output. Themes control the colors used for different semantic roles in help text.

Pass a theme name to .help() or configure it on the program:

// Use a built-in theme
program.help('', { theme: 'ocean' });
ThemeDescription
'default'Cyan commands, green args, yellow types
'ocean'Blue commands, cyan args, green types
'warm'Yellow commands, red args, magenta types
'monochrome'Bold/underline/dim only, no colors

Pass a ColorConfig object to customize individual roles:

import type { ColorConfig } from 'padrone';
const myTheme: ColorConfig = {
command: ['blue', 'bold'],
arg: ['green'],
type: ['yellow'],
description: ['dim'],
label: ['bold'],
meta: ['gray'],
example: ['underline'],
exampleValue: ['italic'],
deprecated: ['strikethrough', 'gray'],
};
program.help('', { theme: myTheme });
RoleDescription
commandCommand names
argArgument/option names
typeType annotations (string, number, etc.)
descriptionDescription text
labelSection labels (Arguments, Commands, etc.)
metaMetadata (defaults, choices, etc.)
exampleExample labels
exampleValueExample values
deprecatedDeprecated items

Each role accepts an array of styles: 'bold', 'dim', 'italic', 'underline', 'strikethrough', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white', 'gray'.

Use the --color global flag or NO_COLOR environment variable:

Terminal window
# Disable colors
myapp --help --color=false
# Or via environment
NO_COLOR=1 myapp --help

Configure stdin reading in the .arguments() meta to pipe data into argument fields.

// Read all stdin as text into 'data' field
.arguments(
z.object({ data: z.string() }),
{ stdin: 'data' }
)
Terminal window
echo "hello" | myapp
# args.data = "hello"

When the target field is an array type, stdin is automatically read line-by-line:

// Read stdin line-by-line into an array (inferred from schema)
.arguments(
z.object({ lines: z.array(z.string()) }),
{ stdin: 'lines' }
)
Terminal window
cat urls.txt | myapp
# args.lines = ["https://...", "https://...", ...]

For large inputs, you can receive stdin as an AsyncIterable instead of buffering everything into memory. Each line is yielded lazily as it arrives.

import { zodAsyncStream } from 'padrone/zod';
// String stream — yields raw lines
.arguments(
z.object({ lines: zodAsyncStream() }),
{ stdin: 'lines' }
)
.action(async ({ lines }) => {
for await (const line of lines) {
process.stdout.write(transform(line) + '\n');
}
})
import { zodAsyncStream, jsonCodec } from 'padrone/zod';
// Typed stream — each line validated through a JSON codec
const recordSchema = z.object({ name: z.string() });
.arguments(
z.object({ records: zodAsyncStream(jsonCodec(recordSchema)) }),
{ stdin: 'records' }
)
.action(async ({ records }) => {
for await (const record of records) {
console.log(record.name);
}
})

The jsonCodec helper wraps a schema to automatically JSON.parse each line before validation. If the input is already an object (not a string), it passes through as-is.

When no stdin is piped, the stream yields nothing (empty iterable). If an item fails validation, the stream throws immediately.

For other Standard Schema libraries, use asyncStream() from padrone directly with .meta():

import { asyncStream } from 'padrone';
// String stream (no item validation)
z.custom<AsyncIterable<string>>().meta(asyncStream())
// With item validation (item schema must handle JSON parsing itself)
z.custom<AsyncIterable<MyType>>().meta(asyncStream(myItemSchema))
  • Only reads when stdin is piped (not a TTY) and the target field wasn’t provided via CLI flags
  • Read mode is inferred from the schema: string fields read all stdin as text, string[] fields read line-by-line, zodAsyncStream()/asyncStream() fields stream lazily
  • Resolution priority: CLI args > stdin > env vars > config files > defaults

The runtime exposes stdin through the PadroneRuntime.stdin interface:

type StdinConfig = {
isTTY: boolean;
text(): Promise<string>;
lines(): AsyncIterable<string>;
};

This is automatically handled when using stdin in arguments meta. For custom runtimes (testing, web), override runtime.stdin to provide mock data.


Create a reusable interceptor with metadata and a factory function. The factory is called fresh per execution, enabling cross-phase state sharing via closures.

import { defineInterceptor } from 'padrone';
// Two-arg form: metadata + factory
const timer = defineInterceptor({ name: 'timer', order: 10 }, () => {
let startTime: number;
return {
start: (ctx, next) => {
startTime = Date.now();
return next();
},
shutdown: (ctx, next) => {
console.log(`Completed in ${Date.now() - startTime}ms`);
return next();
},
};
});
// Single-arg form with chaining (for typed context)
const withDb = defineInterceptor({ name: 'with-db' })
.provides<{ db: Database }>()
.factory(() => ({
execute: (ctx, next) => {
return next({ context: { ...ctx.context, db: createDb() } });
},
}));

Metadata:

PropertyTypeDescription
namestringDisplay name
ordernumberExecution order — lower = outermost (default: 0)
idstringDeduplication key — when multiple interceptors share an id, the last one wins
disabledbooleanSkip this interceptor during execution

Chaining methods (single-arg form):

  • .provides<T>() — Declare what this interceptor adds to the context (type-level only)
  • .requires<T>() — Declare what this interceptor expects on the context (type-level only)
  • .factory(fn) — Set the factory function

Returns: A PadroneInterceptor — pass to .intercept() or use within an extension.


These extensions are available as named exports from 'padrone':

ExportPurpose
padroneEnv(schema)Parse environment variables into args
padroneConfig(options)Load args from config files
padroneProgress(config)Auto-managed progress indicators
padroneCompletion()Shell completion generation
padroneLogger(options)Structured logging with levels
padroneTiming()Execution timing
padroneMan()Man page generation
padroneUpdateCheck(config)Background version checking
padroneMcp()MCP server integration
padroneServe()REST server integration
padroneTracing(config)OpenTelemetry tracing
padroneInk()React (Ink) rendering support

The following extensions are applied automatically by createPadrone() and can be disabled via builtins:

ExportBuiltin keyPurpose
padroneHelp()helpHelp command and --help flag
padroneVersion()versionVersion command and --version flag
padroneRepl()replREPL command and --repl flag
padroneColor()color--color/--no-color support
padroneSuggestions()suggestions”Did you mean?” suggestions
padroneSignalHandling()signalSignal handling and AbortSignal
padroneAutoOutput()autoOutputAuto-print results
padroneStdin()stdinStdin piping support
padroneInteractive()interactiveInteractive prompting

Padrone exports these TypeScript types:

import type {
// Core types
PadroneProgram,
PadroneCommand,
PadroneBuilder,
PadroneExtension,
PadroneParseResult,
PadroneCommandResult,
PadroneAPI,
PadroneSchema,
AsyncPadroneSchema,
PadroneCommandConfig,
// Interceptor types
PadroneInterceptor,
InterceptorBaseContext,
InterceptorStartContext,
InterceptorParseContext,
InterceptorParseResult,
InterceptorValidateContext,
InterceptorValidateResult,
InterceptorExecuteContext,
InterceptorExecuteResult,
InterceptorErrorContext,
InterceptorErrorResult,
InterceptorShutdownContext,
// Runtime types
PadroneRuntime,
ResolvedPadroneRuntime,
InteractivePromptConfig,
PadroneReplPreferences,
PadroneEvalPreferences,
PadroneMcpPreferences,
PadroneServePreferences,
// Progress types
PadroneProgressIndicator,
PadroneProgressConfig,
PadroneProgressMessage,
PadroneProgressOptions,
PadroneSpinnerConfig,
PadroneSpinnerPreset,
// Type utilities
MaybePromise,
OrAsync,
OrAsyncMeta,
HasInteractive,
IsAsyncSchema,
// Inference helpers
InferArgsInput,
InferArgsOutput,
InferCommand,
InferContext,
} from 'padrone';