argv
js/process/argv.ts
fino:process/argv - config-based argv parser with nested command execution.
This module builds small command-line interfaces from plain configuration
objects. A Command describes its options, positional arguments, subcommands,
and handler. Calling parse(argv) walks the command tree, coerces values,
applies defaults, validates required inputs, and then runs the handler for
the final command in the chain.
The parser is intentionally focused on process argument arrays, not shell
strings. Pass an already-tokenized argv array such as process.argv.slice(2);
shell quoting and environment expansion are expected to have happened before
the parser receives the values.
Supported option forms are --long, --long=value, --no-long for boolean
long options, -s, grouped boolean short flags such as -abc, and short
options with inline or following values such as -p8080 or -p 8080.
Multiple long or short aliases may be declared in flags; the first long
name becomes the option key. Boolean options default to false, non-boolean
options default to undefined, and multiple options collect values in
arrays. A literal -- stops option parsing. --help returns the generated
help text for the command being parsed instead of running its handler.
Subcommands can have their own options and positionals. The command handler
receives a CommandContext containing the selected command, root and parent
invocations, parsed named arguments, option values, positional values, the
full invocation chain, and a PromptSession. Defaults may be functions, so a
command can compute context-sensitive defaults or prompt for missing values
before required-option validation runs.
Examples
import { Command } from 'fino:process/argv';
const cli = new Command({
name: 'deploy',
description: 'Deploy an application release.',
options: [
{ flags: '--env, -e', type: 'string', required: true },
{ flags: '--dry-run, -n', type: 'boolean' },
{ flags: '--tag, -t', type: 'string', multiple: true },
],
positionals: [
{ name: 'service', required: true },
],
run(ctx) {
return {
service: ctx.args.service,
environment: ctx.options.env,
dryRun: ctx.options['dry-run'],
tags: ctx.options.tag,
};
},
});
const result = cli.parse(['--env=prod', '-nt', 'blue', 'api']);
import { Command } from 'fino:process/argv';
const cli = new Command({
name: 'tool',
commands: [
{
name: 'user',
commands: [
{
name: 'create',
positionals: [{ name: 'email', required: true }],
options: [{ flags: '--admin, -a', type: 'boolean' }],
run(ctx) {
return `creating ${ctx.args.email}`;
},
},
],
},
],
});
cli.parse(['user', 'create', '--admin', 'me@example.com']);
This parser follows common POSIX/GNU command-line conventions, but it is not a complete clone of any specific CLI framework.
Useful references: - POSIX Utility Syntax Guidelines: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap12.html - GNU option conventions: https://www.gnu.org/prep/standards/html_node/Command_002dLine-Interfaces.html
Interfaces
interface CommandConfig {
Configuration object used to construct a command tree.
import { Command, type CommandConfig } from 'fino:process/argv';
const config: CommandConfig = { name: 'tool', run: () => 'ok' };
const command = new Command(config);
Properties
name?: string
Command name used in help text and subcommand matching.
Root commands may omit a name. Subcommands must provide one or construction throws when they are registered.
import type { CommandConfig } from 'fino:process/argv';
const config: CommandConfig = { name: 'deploy' };
description?: string
Human-readable command description shown by help().
When omitted, help output contains only usage, options, arguments, and subcommands.
import type { CommandConfig } from 'fino:process/argv';
const config: CommandConfig = { description: 'Deploy a service.' };
allowUnknown?: boolean
Whether unknown options should be treated as positionals.
Defaults to false. When false, parsing an unknown --long or -s
option throws for the current command.
import type { CommandConfig } from 'fino:process/argv';
const config: CommandConfig = { allowUnknown: true };
stopOptionsAfterPositionals?: boolean
Whether option parsing should stop after the first positional token.
Defaults to false. Set this for commands like script runners where every
token after the script path belongs to the script, even if it looks like an
option recognized by the runner itself.
allowHelp?: boolean
Whether --help should be consumed by this command.
Defaults to true. Set this to false for dispatcher commands that need
to forward --help to another parser through a positional passthrough.
import type { CommandConfig } from 'fino:process/argv';
const config: CommandConfig = { allowHelp: false, allowUnknown: true };
run?: (ctx: CommandContext) => unknown
Handler invoked after parsing and validation.
If omitted, the command returns its help text. The handler may return any value or a promise.
import type { CommandConfig } from 'fino:process/argv';
const config: CommandConfig = { run: (ctx) => ctx.options };
options?: OptionConfig[]
Option definitions for this command.
Options are scoped to the command where they are declared. Parent and child command options are stored on their respective invocation nodes.
import type { CommandConfig } from 'fino:process/argv';
const config: CommandConfig = {
options: [{ flags: '--verbose, -v', type: 'boolean' }],
};
positionals?: PositionalConfig[]
Positional argument definitions for this command.
Values are coerced in declaration order. At most one multiple positional
is allowed, and it must be declared last.
import type { CommandConfig } from 'fino:process/argv';
const config: CommandConfig = {
positionals: [{ name: 'service', required: true }],
};
commands?: Array<Command | CommandConfig>
Nested subcommands.
Each subcommand may be a Command instance or a config object. Duplicate
child names throw during construction.
import type { CommandConfig } from 'fino:process/argv';
const config: CommandConfig = {
commands: [{ name: 'init', run: () => 'created' }],
};
interface OptionConfig {
Command-line option definition, including flags, type, multiplicity, and defaults.
import type { OptionConfig } from 'fino:process/argv';
const option: OptionConfig = { flags: '--port, -p', type: 'number' };
Properties
flags: string
Comma-separated long and/or short flags.
Long flags start with --; short flags start with - and must be one
character. Multiple aliases are accepted. At least one valid flag is
required, and the first long flag becomes the option key when present.
import type { OptionConfig } from 'fino:process/argv';
const option: OptionConfig = { flags: '--env, -e', type: 'string' };
type?: 'boolean' | 'string' | 'number'
Scalar type used for value coercion.
Defaults to boolean. Number coercion rejects non-finite values.
import type { OptionConfig } from 'fino:process/argv';
const option: OptionConfig = { flags: '--retries', type: 'number' };
choices?: Array<string | number>
Allowed values after scalar type coercion.
String choices compare with string values and number choices compare with numeric values. Invalid values throw during parsing.
import type { OptionConfig } from 'fino:process/argv';
const option: OptionConfig = { flags: '--env', type: 'string', choices: ['dev', 'prod'] };
multiple?: boolean
Whether the option may be provided multiple times.
Defaults to false. Multiple options collect values into an array, with an
empty array as the default when no explicit default is supplied.
import type { OptionConfig } from 'fino:process/argv';
const option: OptionConfig = { flags: '--tag, -t', type: 'string', multiple: true };
required?: boolean
Whether the option must be provided or resolved by a default.
Defaults to false. Required validation runs after default functions have
resolved.
import type { OptionConfig } from 'fino:process/argv';
const option: OptionConfig = { flags: '--env', type: 'string', required: true };
description?: string
Description shown in generated help output.
Omitted descriptions leave the help row without trailing explanatory text.
import type { OptionConfig } from 'fino:process/argv';
const option: OptionConfig = { flags: '--env', description: 'Deployment environment.' };
default?: OptionDefault
Default value or function used when the option is not provided.
Function defaults receive the current command context and may return a promise. Array defaults are cloned before use.
import type { OptionConfig } from 'fino:process/argv';
const option: OptionConfig = { flags: '--env', type: 'string', default: 'dev' };
interface PositionalConfig {
Positional argument definition for a command.
import type { PositionalConfig } from 'fino:process/argv';
const positional: PositionalConfig = { name: 'file', required: true };
Properties
name: string
Positional argument name.
Names must be non-empty. Parsed values are exposed as ctx.args[name].
import type { PositionalConfig } from 'fino:process/argv';
const positional: PositionalConfig = { name: 'service' };
type?: 'string' | 'number'
Scalar type used for value coercion.
Defaults to string. Number coercion rejects non-finite values.
import type { PositionalConfig } from 'fino:process/argv';
const positional: PositionalConfig = { name: 'count', type: 'number' };
choices?: Array<string | number>
Allowed values after positional type coercion.
import type { PositionalConfig } from 'fino:process/argv';
const positional: PositionalConfig = { name: 'target', choices: ['api', 'worker'] };
required?: boolean
Whether a value is required.
Defaults to false. Missing required positionals throw unless parsing is
still descending into a subcommand.
import type { PositionalConfig } from 'fino:process/argv';
const positional: PositionalConfig = { name: 'file', required: true };
multiple?: boolean
Whether this positional consumes all remaining positional values.
Defaults to false. Only one multiple positional is allowed and it must be
the final positional definition.
import type { PositionalConfig } from 'fino:process/argv';
const positional: PositionalConfig = { name: 'files', multiple: true };
description?: string
Description shown in generated help output.
import type { PositionalConfig } from 'fino:process/argv';
const positional: PositionalConfig = { name: 'file', description: 'File to read.' };
interface CommandContext {
Runtime context passed to a command handler.
import { Command, type CommandContext } from 'fino:process/argv';
const cli = new Command({
run(ctx: CommandContext) {
return ctx.path.join(' ');
},
});
Properties
command: Command
Final command whose handler is running.
import type { CommandContext } from 'fino:process/argv';
function run(ctx: CommandContext) {
return ctx.command.name;
}
invocation: CommandInvocation
Invocation node for the final command.
This contains parsed values scoped to the selected command only.
import type { CommandContext } from 'fino:process/argv';
function run(ctx: CommandContext) {
return ctx.invocation.options;
}
parent: CommandInvocation | null
Parent invocation, or null for the root command.
import type { CommandContext } from 'fino:process/argv';
function run(ctx: CommandContext) {
return ctx.parent?.name ?? 'root';
}
root: CommandInvocation
Root invocation for the parsed command chain.
import type { CommandContext } from 'fino:process/argv';
function run(ctx: CommandContext) {
return ctx.root.args;
}
path: string[]
Selected command path as an array of command names.
Unnamed root commands do not contribute a path segment.
import type { CommandContext } from 'fino:process/argv';
function run(ctx: CommandContext) {
return ctx.path.join(':');
}
args: Record<string, unknown>
Parsed named positional values for the final command.
Missing optional positionals are present with undefined values. Variadic
positionals are arrays.
import type { CommandContext } from 'fino:process/argv';
function run(ctx: CommandContext) {
return ctx.args.service;
}
options: Record<string, unknown>
Parsed option values for the final command.
Boolean options default to false, non-boolean options default to
undefined, and multiple options default to arrays.
import type { CommandContext } from 'fino:process/argv';
function run(ctx: CommandContext) {
return ctx.options.verbose;
}
positionals: unknown[]
Positional values for the final command in declaration/order form.
Extra positionals accepted through allowUnknown are appended after named
positional values.
import type { CommandContext } from 'fino:process/argv';
function run(ctx: CommandContext) {
return ctx.positionals.length;
}
chain: CommandInvocation[]
Invocation chain from root to final command.
Use this to inspect parent command options in nested CLIs.
import type { CommandContext } from 'fino:process/argv';
function run(ctx: CommandContext) {
return ctx.chain.map((item) => item.name);
}
prompt: PromptSession
Prompt session available to handlers and default functions.
Defaults to createDefaultPrompt() unless a prompt is supplied to
Command.parse().
import type { CommandContext } from 'fino:process/argv';
async function run(ctx: CommandContext) {
return ctx.prompt;
}
providedOptions: Set<string>
Set of option keys explicitly provided for the final command.
Defaults do not add keys to this set.
import type { CommandContext } from 'fino:process/argv';
function run(ctx: CommandContext) {
return ctx.providedOptions.has('env');
}
Methods
optionProvided(key: string): boolean
Check whether an option key was explicitly provided.
This is equivalent to providedOptions.has(key) for the final invocation.
It returns false for values supplied by defaults.
import type { CommandContext } from 'fino:process/argv';
function run(ctx: CommandContext) {
return ctx.optionProvided('dry-run');
}
interface ParseOptions {
Parse-time dependencies for Command.parse().
Properties
prompt?: PromptSession
Classes
class CommandInvocation {
Parsed invocation node for one command in a nested command chain.
Each node stores parsed values scoped to one command and points at its parent
invocation. Handlers usually receive these through CommandContext.
import { CommandInvocation, Command } from 'fino:process/argv';
const command = new Command({ name: 'root' });
const invocation = new CommandInvocation(command, null, {}, {}, [], new Set());
Properties
command: Command
Command represented by this invocation.
import { Command, CommandInvocation } from 'fino:process/argv';
const command = new Command({ name: 'tool' });
const invocation = new CommandInvocation(command, null, {}, {}, [], new Set());
console.log(invocation.command.name);
parent: CommandInvocation | null
Parent invocation, or null for the root.
import { Command, CommandInvocation } from 'fino:process/argv';
const root = new CommandInvocation(new Command({ name: 'tool' }), null, {}, {}, [], new Set());
const child = new CommandInvocation(new Command({ name: 'run' }), root, {}, {}, [], new Set());
console.log(child.parent?.name);
args: Record<string, unknown>
Parsed named positionals for this command.
import { Command, CommandInvocation } from 'fino:process/argv';
const invocation = new CommandInvocation(new Command(), null, { file: 'a.txt' }, {}, ['a.txt'], new Set());
console.log(invocation.args.file);
options: Record<string, unknown>
Parsed options for this command.
import { Command, CommandInvocation } from 'fino:process/argv';
const invocation = new CommandInvocation(new Command(), null, {}, { verbose: true }, [], new Set(['verbose']));
console.log(invocation.options.verbose);
positionals: unknown[]
Parsed positional values for this command in order.
import { Command, CommandInvocation } from 'fino:process/argv';
const invocation = new CommandInvocation(new Command(), null, {}, {}, ['a.txt'], new Set());
console.log(invocation.positionals[0]);
providedOptions: Set<string>
Option keys explicitly provided for this command.
Defaults are not included in this set.
import { Command, CommandInvocation } from 'fino:process/argv';
const invocation = new CommandInvocation(new Command(), null, {}, {}, [], new Set(['env']));
console.log(invocation.providedOptions.has('env'));
Constructors
constructor(
command: Command,
parent: CommandInvocation | null,
args: Record<string, unknown>,
options: Record<string, unknown>,
positionals: unknown[],
providedOptions: Set<string>
)
Create a parsed invocation node.
The constructor stores its arguments directly. It does not validate that the values match the command's definitions.
import { Command, CommandInvocation } from 'fino:process/argv';
const invocation = new CommandInvocation(new Command({ name: 'tool' }), null, {}, {}, [], new Set());
Getters
get name(): string | null
Command name for this invocation, or null for unnamed commands.
import { Command, CommandInvocation } from 'fino:process/argv';
const invocation = new CommandInvocation(new Command({ name: 'deploy' }), null, {}, {}, [], new Set());
console.log(invocation.name);
get path(): string[]
Command path from root to this invocation.
Unnamed commands are skipped. The returned array is newly built for each access.
import { Command, CommandInvocation } from 'fino:process/argv';
const root = new CommandInvocation(new Command({ name: 'tool' }), null, {}, {}, [], new Set());
const child = new CommandInvocation(new Command({ name: 'deploy' }), root, {}, {}, [], new Set());
console.log(child.path.join(' '));
class Command {
Config-based command with nested subcommands, options, and positionals.
A Command parses tokenized argv arrays, applies defaults, validates
required values, and runs the selected command handler. parse() returns the
handler result or a promise if async defaults or handlers are used.
import { Command } from 'fino:process/argv';
const cli = new Command({
name: 'echo',
positionals: [{ name: 'message', required: true }],
run: (ctx) => ctx.args.message,
});
cli.parse(['hello']);
Constructors
constructor(config: CommandConfig = {})
Create a command from configuration.
Options and positionals are registered immediately. Construction throws for invalid flags, duplicate subcommands, unnamed subcommands, empty positional names, or multiple/incorrectly placed variadic positionals.
import { Command } from 'fino:process/argv';
const command = new Command({ name: 'deploy', options: [{ flags: '--env' }] });
Getters
get name(): string | null
Command name, or null for unnamed root commands.
The value is used in generated usage text and subcommand matching.
import { Command } from 'fino:process/argv';
console.log(new Command({ name: 'deploy' }).name);
get description(): string | undefined
Description shown in generated help output.
Returns undefined when no description was configured.
import { Command } from 'fino:process/argv';
console.log(new Command({ description: 'Deploy services.' }).description);
get parent(): Command | null
Parent command, or null for the root.
The parent is assigned when a command is registered as a subcommand.
import { Command } from 'fino:process/argv';
const child = new Command({ name: 'run' });
new Command({ name: 'tool', commands: [child] });
console.log(child.parent?.name);
Methods
parse(argv: string[], options: ParseOptions = {}): unknown
Parse tokenized argv and run the selected command.
--help returns generated help text instead of running a handler. Unknown
options throw unless allowUnknown is enabled for the current command.
Async default values make the return value promise-like.
import { Command } from 'fino:process/argv';
const cli = new Command({
options: [{ flags: '--count, -c', type: 'number', default: 1 }],
run: (ctx) => ctx.options.count,
});
const count = cli.parse(['--count=3']);
run(ctx: CommandContext): unknown
Run this command's handler with a parsed context.
If no handler was configured, returns this command's help text. This method
does not parse or validate argv; parse() performs those steps.
import { Command, CommandInvocation } from 'fino:process/argv';
const command = new Command({ run: (ctx) => ctx.path });
const invocation = new CommandInvocation(command, null, {}, {}, [], new Set());
command.run({
command,
invocation,
parent: null,
root: invocation,
path: [],
args: {},
options: {},
positionals: [],
chain: [invocation],
prompt: undefined as never,
providedOptions: new Set(),
optionProvided: () => false,
});
usage(programName?: string): string
Generate one-line usage text.
The output includes command path, [options] when relevant, positional
usage, and [command] when subcommands are available.
import { Command } from 'fino:process/argv';
const cli = new Command({ name: 'tool', positionals: [{ name: 'file' }] });
console.log(cli.usage());
help(programName?: string): string
Generate help text for this command.
The result includes usage, description, options, positional arguments, and child commands when present. It never runs the command handler.
import { Command } from 'fino:process/argv';
const cli = new Command({ name: 'tool', description: 'Example CLI.' });
console.log(cli.help());