Create type-safe, interactive CLI apps with Zod schemas
- π Type-safe - Full TypeScript support with Zod schema validation
- π― Fluent API - Chain commands, arguments, and options with a clean builder pattern
- π€ AI-Ready - First-class support for Vercel AI SDK tool integration
- π Auto Help - Automatic help generation from your schema definitions
- π§© Nested Commands - Support for deeply nested subcommands
- π Standard Schema - Built on Standard Schema for maximum compatibility
- π Zero Config - Works out of the box with sensible defaults
# Using npm
npm install padrone zod
# Using bun
bun add padrone zod
# Using pnpm
pnpm add padrone zod
# Using yarn
yarn add padrone zodimport { createPadrone } from 'padrone';
import * as z from 'zod/v4';
const program = createPadrone('myapp')
.command('greet', (c) =>
c
.options(
z.object({
names: z.array(z.string()).describe('Names to greet'),
prefix: z
.string()
.optional()
.describe('Prefix to use in greeting')
.meta({ alias: 'p' }),
}),
{ positional: ['...names'] },
)
.action((options) => {
const prefix = options?.prefix ? `${options.prefix} ` : '';
options.names.forEach((name) => {
console.log(`Hello, ${prefix}${name}!`);
});
}),
);
// Run from CLI arguments
program.cli();# Run with arguments
myapp greet John Jane --prefix Mr.
# Or with alias
myapp greet John Jane -p Mr.Output:
Hello, Mr. John!
Hello, Mr. Jane!
// Run a command directly with typed options
program.run('greet', { names: ['John', 'Jane'], prefix: 'Dr.' });
// Parse CLI input without executing
const parsed = program.parse('greet John --prefix Mr.');
console.log(parsed.options); // { names: ['John'], prefix: 'Mr.' }Generate a typed API from your CLI program:
const api = program.api();
// Call commands as functions with full type safety
api.greet({ names: ['Alice', 'Bob'], prefix: 'Dr.' });const program = createPadrone('weather')
.command('forecast', (c) =>
c
.options(
z.object({
city: z.string().describe('City name'),
days: z.number().optional().default(3).describe('Number of days'),
}),
{ positional: ['city'] },
)
.action((options) => {
console.log(`Forecast for ${options.city}: ${options.days} days`);
})
.command('extended', (c) =>
c
.options(
z.object({
city: z.string().describe('City name'),
}),
{ positional: ['city'] },
)
.action((options) => {
console.log(`Extended forecast for ${options.city}`);
}),
),
);
// Run nested command
program.cli('forecast extended London');const program = createPadrone('app')
.command('serve', (c) =>
c
.options(
z.object({
port: z
.number()
.default(3000)
.describe('Port to listen on')
.meta({ alias: 'p', examples: ['3000', '8080'] }),
host: z
.string()
.default('localhost')
.describe('Host to bind to')
.meta({ alias: 'h' }),
verbose: z
.boolean()
.optional()
.describe('Enable verbose logging')
.meta({ alias: 'v', deprecated: 'Use --debug instead' }),
}),
)
.action((options) => {
console.log(`Server running at ${options.host}:${options.port}`);
}),
);Padrone supports loading options from environment variables and config files using dedicated schema methods:
const program = createPadrone('app')
.command('serve', (c) =>
c
.options(
z.object({
port: z.number().default(3000).describe('Port to listen on'),
apiKey: z.string().describe('API key for authentication'),
}),
)
// Map environment variables to options
.env(
z
.object({
APP_PORT: z.coerce.number().optional(),
API_KEY: z.string().optional(),
})
.transform((env) => ({
port: env.APP_PORT,
apiKey: env.API_KEY,
})),
)
// Load config from JSON file with matching schema
.configFile(
'app.config.json',
z.object({
port: z.number().optional(),
apiKey: z.string().optional(),
}),
)
.action((options) => {
console.log(`Server running on port ${options.port}`);
}),
);Precedence order (highest to lowest): CLI args > environment variables > config file
Padrone provides first-class support for the Vercel AI SDK, making it easy to expose your CLI as an AI tool:
import { streamText } from 'ai';
import { createPadrone } from 'padrone';
import * as z from 'zod/v4';
const weatherCli = createPadrone('weather')
.command('current', (c) =>
c
.options(
z.object({
city: z.string().describe('City name'),
}),
{ positional: ['city'] },
)
.action((options) => {
return { city: options.city, temperature: 72, condition: 'Sunny' };
}),
);
// Convert your CLI to an AI tool
const result = await streamText({
model: yourModel,
prompt: "What's the weather in London?",
tools: {
weather: weatherCli.tool(),
},
});Padrone automatically generates help text from your Zod schemas:
console.log(program.help());Example output:
Usage: myapp greet [names...] [options]
Arguments:
names... Names to greet
Options:
-p, --prefix <string> Prefix to use in greeting
-h, --help Show help
Creates a new CLI program with the given name.
| Method | Description |
|---|---|
.configure(config) |
Configure program properties (title, description, version) |
.command(name, builder) |
Add a command to the program |
.options(schema, meta?) |
Define options schema with optional positional args |
.env(schema) |
Define schema for parsing environment variables into options |
.configFile(file, schema?) |
Configure config file path(s) and schema |
.action(handler) |
Set the command handler function |
.cli(input?) |
Run as CLI (parses process.argv or input string) |
.run(command, options) |
Run a command programmatically |
.parse(input?) |
Parse input without executing |
.stringify(command?, options?) |
Convert command and options back to CLI string |
.api() |
Generate a typed API object |
.help(command?) |
Generate help text |
.tool() |
Generate a Vercel AI SDK tool |
.find(command) |
Find a command by name |
Use the second argument of .options() to configure positional arguments and per-option metadata:
.options(schema, {
positional: ['source', '...files', 'dest'], // '...files' is variadic
options: {
verbose: { alias: 'v' },
format: { deprecated: 'Use --output instead' },
},
})Use .meta() on Zod schemas to provide additional CLI metadata:
z.string().meta({
alias: 'p', // Short alias (-p)
examples: ['value'], // Example values for help text
deprecated: 'message', // Mark as deprecated
hidden: true, // Hide from help output
})- Node.js 18+ or Bun
- TypeScript 5.0+ (recommended)
- Zod 3.25+ or 4.x
MIT Β© Gokhan Kurt
Made with β€οΈ by Gokhan Kurt