Package Exports
- cli-args-parser
Readme
cli-args-parser
Expressive CLI Argument Parser
Expressive syntax meets powerful schema validation.
Zero dependencies • TypeScript-first • Nested subcommands • Custom validation
Parse key=value, key:=typed, Key:Meta — with fully customizable separators.
Quick Start · Syntax · Schema · Validation · CLI · API
Quick Start
npm install cli-args-parser
# or
pnpm add cli-args-parserimport { parse } from 'cli-args-parser'
const result = parse([
'https://api.example.com',
'name=Filipe',
'age:=35',
'active:=true',
'Authorization:Bearer TOKEN',
'--verbose',
'-o', 'output.json'
])
// {
// positional: ['https://api.example.com'],
// data: { name: 'Filipe', age: 35, active: true },
// meta: { Authorization: 'Bearer TOKEN' },
// flags: { verbose: true },
// options: { o: 'output.json' },
// errors: []
// }Table of Contents
- Features
- Syntax Reference
- Schema Parser
- Custom Validation
- Auto-Short Generation
- CLI with Subcommands
- Shell Completion
- Formatter & Theming
- Additional Features
- API Reference
- License
Features
| Category | Features |
|---|---|
| Syntax | Expressive key=value, key:=typed, Key:Value patterns |
| Separators | Fully customizable — define your own patterns and categories |
| Schema | Type coercion, required fields, choices, defaults, env vars |
| Validation | Built-in + custom validation functions |
| Commands | Nested subcommands with unlimited depth |
| Options | Auto-short generation, aliases, negation (--no-flag) |
| Output | Shell completion (Bash, Zsh, Fish), help generation |
| Formatting | Token-based theming, bring your own colors (chalk, picocolors, tuiuiu.js/colors, ANSI) |
| Quality | 431 tests, zero dependencies, TypeScript-first |
Syntax Reference
Default Separators
| Syntax | Category | Example | Result |
|---|---|---|---|
key=value |
data |
name=Filipe |
data.name = "Filipe" |
key:=value |
data (typed) |
age:=35 |
data.age = 35 |
Key:Value |
meta |
Auth:Bearer X |
meta.Auth = "Bearer X" |
--flag |
flags |
--verbose |
flags.verbose = true |
--no-flag |
flags* |
--no-color |
flags.color = false |
-f |
flags |
-v |
flags.v = true |
--opt=val |
options |
--output=file |
options.output = "file" |
-o val |
options |
-o file |
options.o = "file" |
-- |
marker | -- --help |
End of options, rest is positional |
Notes:
- The
Key:Valuemeta syntax requires the key to start with an uppercase letter (e.g.,Authorization:Bearer). Lowercase keys likenginx:latestare treated as positional to avoid conflicts with docker images and similar patterns.- Keys containing
/are rejected for=and:=separators to avoid matching URL-like patterns (e.g.,data:text/plain;base64,x=). Use positional arguments for paths:myapp -- path/to/fileinstead ofpath/to=file. Namespaced keys likeaws:region=valuework because they don't contain/.- *
--no-flagbehavior differs between APIs:
parse(): Always goes toflagsasflags.flag = falsecreateParser()/createCLI(): Goes tooptionsasoptions.flag = false(only for defined boolean options withnegatable !== false)
End of Options (--)
The -- marker signals the end of options. Everything after it is treated as positional, even if it looks like a flag or separator:
const result = parse(['--verbose', '--', '--help', 'name=value'])
// result.flags = { verbose: true }
// result.positional = ['--help', 'name=value'] // Treated as positional, not parsedThis is useful for passing arguments to subprocesses or handling filenames that start with -.
Behavior with createParser() and allowUnknown:
When using createParser() with positional definitions:
const parser = createParser({
positional: [{ name: 'file' }],
allowUnknown: true // default
})
// Without --: unknown flags go to options
parser.parse(['--unknown'])
// { options: { unknown: true }, positional: {}, rest: [] }
// With --: everything after is positional, fills schema positions first, then rest
parser.parse(['--', '--unknown', 'extra'])
// { positional: { file: '--unknown' }, rest: ['extra'], options: {} }With allowUnknown: false, extra positionals after -- that don't fit schema positions are silently ignored:
const parser = createParser({
positional: [{ name: 'file' }],
allowUnknown: false
})
parser.parse(['--', 'file.txt', 'extra'])
// { positional: { file: 'file.txt' }, rest: [], options: {} }
// 'extra' is ignored, not stored in restNote:
createCLI()usesstrictinstead ofallowUnknown. Withstrict: true, unknown options cause errors, but extras after--always go torest(useful for passing args to subprocesses).
Type Coercion (:=)
The := separator automatically coerces values to their JavaScript types:
parse(['count:=42']) // data.count = 42 (number)
parse(['active:=true']) // data.active = true (boolean)
parse(['active:=false']) // data.active = false (boolean)
parse(['value:=null']) // data.value = null
parse(['items:=[1,2,3]']) // data.items = [1, 2, 3] (JSON array)
parse(['config:={"a":1}']) // data.config = { a: 1 } (JSON object)
parse(['tags:=a,b,c']) // data.tags = ["a", "b", "c"] (comma-separated)
parse(['ids:=1,2,3']) // data.ids = [1, 2, 3] (comma-separated with type coercion)Customizable Separators
The separator system is fully customizable. Define your own patterns and category names:
import { parse } from 'cli-args-parser'
// Default separators
const defaults = {
'=': 'data', // key=value → data.key
':=': { to: 'data', typed: true }, // key:=value → data.key (with type coercion)
':': 'meta' // Key:Value → meta.Key
}
// Custom separators for your use case
const result = parse(['file@data.json', 'name->Filipe', 'count::42'], {
separators: {
'@': 'files', // file@path → files.file
'->': 'body', // key->value → body.key
'::': { to: 'body', typed: true } // key::value → body.key (typed)
}
})
// {
// positional: [],
// files: { file: 'data.json' },
// body: { name: 'Filipe', count: 42 },
// flags: {},
// options: {},
// errors: []
// }Separator Configuration:
| Format | Description | Example |
|---|---|---|
string |
Category name shorthand | '@': 'files' |
{ to, typed? } |
Full config with type coercion | ':=': { to: 'data', typed: true } |
{ to, prefix: true } |
Prefix separator (starts the arg) | '@': { to: 'tags', prefix: true } |
Prefix Separators
Regular separators expect key<sep>value format. Prefix separators start at position 0 and use : as the key/value delimiter:
import { parse } from 'cli-args-parser'
const result = parse(['@tag:important', '@env:production', 'name=test'], {
separators: {
'=': 'data',
'@': { to: 'tags', prefix: true } // @key:value format
}
})
// {
// positional: [],
// data: { name: 'test' },
// tags: { tag: 'important', env: 'production' },
// flags: {},
// options: {},
// errors: []
// }Prefix separators are useful for tag-like syntax where you want a marker character to distinguish a category.
Schema Parser
Add validation, defaults, and help generation:
import { createParser } from 'cli-args-parser'
const parser = createParser({
positional: [
{ name: 'url', required: true, description: 'Target URL' }
],
options: {
output: {
short: 'o',
type: 'string',
description: 'Output file'
},
verbose: {
short: 'v',
type: 'boolean',
default: false
},
format: {
type: 'string',
choices: ['json', 'yaml', 'xml'],
default: 'json'
},
retries: {
type: 'number',
default: 3,
env: 'MAX_RETRIES' // Fallback to env var
}
}
})
const result = parser.parse(['https://api.com', '-v', '--format=yaml'])
console.log(parser.help())Option Definition
interface OptionDefinition {
short?: string // Short flag (-o)
aliases?: string[] // Alternative names
type?: 'string' | 'number' | 'boolean' | 'array'
default?: any // Default value
description?: string // Help text
required?: boolean // Required option
choices?: any[] // Allowed values
env?: string // Environment variable fallback
hidden?: boolean // Hide from help
negatable?: boolean // Allow --no-flag (default: true)
validate?: ValidateFn // Custom validation function
}Positional Definition
interface PositionalDefinition {
name: string // Argument name
description?: string // Help text
required?: boolean // Required argument
type?: 'string' | 'number' | 'boolean' | 'array'
default?: any // Default value
variadic?: boolean // Capture remaining args
validate?: ValidateFn // Custom validation function
}Custom Validation
Add custom validation logic to options and positionals:
import { createParser, ValidateFn } from 'cli-args-parser'
const parser = createParser({
options: {
port: {
type: 'number',
validate: (value) => {
const num = value as number
if (num < 1 || num > 65535) {
return `Port must be between 1 and 65535, got ${num}`
}
return true
}
},
email: {
type: 'string',
validate: (value) => {
const str = value as string
if (!str.includes('@')) {
return 'Invalid email format'
}
return true
}
}
},
positional: [
{
name: 'url',
required: true,
validate: (value) => {
const str = value as string
if (!str.startsWith('https://')) {
return 'URL must use HTTPS'
}
return true
}
}
]
})
const result = parser.parse(['http://example.com', '--port=70000'])
// result.errors = [
// 'URL must use HTTPS',
// 'Port must be between 1 and 65535, got 70000'
// ]Validation Function
// Return true if valid, or error message string if invalid
type ValidationResult = true | string
type ValidateFn<T = PrimitiveValue | PrimitiveValue[]> = (value: T) => ValidationResultValidation Behavior:
- Runs after type coercion
- Only runs if value is defined (not on
undefined) - Combines with
choicesvalidation (choices checked first) - Errors are collected in
result.errors
Combining Validations
const parser = createParser({
options: {
level: {
type: 'number',
choices: [1, 2, 3, 4, 5], // First check: must be in choices
validate: (value) => { // Second check: custom logic
if (value === 3) return 'Level 3 is temporarily disabled'
return true
}
}
}
})
parser.parse(['--level=10']) // Error: Invalid value (choices)
parser.parse(['--level=3']) // Error: Level 3 is temporarily disabled
parser.parse(['--level=2']) // OKAuto-Short Generation
Automatically assigns short flags to options:
const parser = createParser({
autoShort: true,
options: {
verbose: { type: 'boolean' }, // Gets -v
output: { type: 'string' }, // Gets -o
format: { type: 'string' } // Gets -f
}
})
parser.parse(['-v', '-o', 'file.txt', '-f', 'json'])Algorithm:
- Options sorted alphabetically (deterministic assignment)
- Try first letter lowercase (
verbose→v) - If taken, try uppercase (
verbose→V) - If taken, try next letter (
verbose→e)
CLI with Subcommands
Build complex CLIs with nested command routing:
import { createCLI } from 'cli-args-parser'
const cli = createCLI({
name: 'myapp',
version: '1.0.0',
autoShort: true,
options: {
verbose: { type: 'boolean', description: 'Verbose output' }
},
commands: {
get: {
description: 'Fetch a resource',
positional: [{ name: 'url', required: true }],
options: {
output: { short: 'o', type: 'string' }
}
},
post: {
description: 'Create a resource',
positional: [{ name: 'url', required: true }]
}
}
})
cli.parse(['get', 'https://api.com', '-o', 'result.json', '--verbose'])Nested Subcommands
Commands can be nested to any depth:
const cli = createCLI({
name: 'kubectl',
commands: {
config: {
description: 'Manage configuration',
commands: {
get: {
description: 'Get config value',
positional: [{ name: 'key', required: true }]
},
context: {
description: 'Manage contexts',
commands: {
list: { description: 'List all contexts' },
use: {
description: 'Switch context',
positional: [{ name: 'name', required: true }]
}
}
}
}
}
}
})
cli.parse(['config', 'get', 'theme'])
// → command: ['config', 'get'], positional: { key: 'theme' }
cli.parse(['config', 'context', 'use', 'production'])
// → command: ['config', 'context', 'use'], positional: { name: 'production' }Command Handlers
Execute code when a command is matched:
const cli = createCLI({
name: 'deploy',
commands: {
staging: {
description: 'Deploy to staging',
handler: async (result) => {
console.log('Deploying to staging...')
// Your deploy logic
}
},
production: {
description: 'Deploy to production',
options: {
force: { type: 'boolean', default: false }
},
handler: async (result) => {
if (!result.options.force) {
console.log('Use --force to deploy to production')
return
}
console.log('Deploying to production...')
}
}
}
})
await cli.run(process.argv.slice(2))Command Aliases
commands: {
install: {
aliases: ['i', 'add'],
description: 'Install packages'
}
}
// All equivalent:
// myapp install lodash
// myapp i lodash
// myapp add lodashValidation in Commands
Custom validation works in subcommands too:
const cli = createCLI({
name: 'server',
commands: {
start: {
description: 'Start the server',
options: {
port: {
type: 'number',
default: 3000,
validate: (value) => {
if (value < 1024) return 'Port must be >= 1024 (non-privileged)'
return true
}
}
},
positional: [
{
name: 'config',
validate: (value) => {
if (!value.endsWith('.json')) return 'Config must be a .json file'
return true
}
}
]
}
}
})Version Command
When you define a version in your CLI schema, automatic version handling is enabled:
const cli = createCLI({
name: 'myapp',
version: '1.0.0',
description: 'My awesome CLI'
})
// All of these work:
// myapp version
// myapp --version
// myapp -VOutput:
myapp 1.0.0
My awesome CLIYou can also call cli.version() programmatically:
console.log(cli.version())The version output respects the formatter configuration (see Formatter & Theming).
Shell Completion
Generate completion scripts for popular shells:
const cli = createCLI({ /* ... */ })
// Generate for your shell
const bashScript = cli.completion('bash')
const zshScript = cli.completion('zsh')
const fishScript = cli.completion('fish')# Add to ~/.bashrc or ~/.zshrc
eval "$(myapp completion bash)"Formatter & Theming
The library outputs structured tokens that you can style with any coloring library. Zero colors bundled — bring your own chalk, picocolors, or raw ANSI codes.
Disabling Colors
All CLIs created with createCLI() automatically support the --no-color flag and respect the NO_COLOR standard:
# Disable colors via flag
myapp --help --no-color
# Enable colors explicitly
myapp --help --color
# Disable colors via environment variable
NO_COLOR=1 myapp --help
# Force colors (overrides NO_COLOR)
FORCE_COLOR=1 myapp --helpPriority (highest to lowest):
--no-color/--colorCLI flagsFORCE_COLORenvironment variableNO_COLORenvironment variable- Default: colors enabled
Programmatic Usage:
import { shouldUseColor, getEffectiveFormatter, type Formatter } from 'cli-args-parser'
// Check if colors should be used (respects env vars)
if (shouldUseColor()) {
console.log('Colors enabled')
}
// Get effective formatter (returns undefined if colors disabled)
const myFormatter: Formatter = { 'error-message': s => `\x1b[31m${s}\x1b[0m` }
const effectiveFormatter = getEffectiveFormatter(myFormatter)
// effectiveFormatter is undefined when NO_COLOR is setUsing with Chalk
import chalk from 'chalk'
import { createCLI, type Formatter } from 'cli-args-parser'
const cli = createCLI({
name: 'myapp',
version: '1.0.0',
commands: {
deploy: { description: 'Deploy to production' }
},
options: {
verbose: { short: 'v', type: 'boolean', description: 'Verbose output' }
},
formatter: {
'section-header': s => chalk.bold.white(s),
'program-name': s => chalk.magenta.bold(s),
'version': s => chalk.green(s),
'command-name': s => chalk.yellow(s),
// Command options
'option-flag': s => chalk.green(s),
'option-type': s => chalk.cyan(s),
'option-default': s => chalk.dim(s),
// Global options (same style or different)
'global-option-flag': s => chalk.green(s),
'global-option-type': s => chalk.cyan(s),
'global-option-default': s => chalk.dim(s),
'error-message': s => chalk.red(s)
}
})
console.log(cli.help()) // Colorized help
console.log(cli.version()) // Colorized versionUsing with Picocolors
Picocolors is 2x faster and 14x smaller than chalk:
import pc from 'picocolors'
import { createCLI, type Formatter } from 'cli-args-parser'
// Create a reusable theme
const theme: Formatter = {
'section-header': s => pc.bold(s),
'program-name': s => pc.magenta(pc.bold(s)),
'command-name': s => pc.yellow(s),
// Command options
'option-flag': s => pc.green(s),
'option-type': s => pc.cyan(s),
'option-default': s => pc.dim(s),
// Global options
'global-option-flag': s => pc.green(s),
'global-option-type': s => pc.cyan(s),
'global-option-default': s => pc.dim(s),
'error-message': s => pc.red(s)
}
const cli = createCLI({
name: 'api',
version: '1.0.0',
formatter: theme
})Using with tuiuiu.js/colors
tuiuiu.js/colors is a zero-dependency color library with a chainable API, template literals, and style composition — perfect for zero-dep CLIs:
| Feature | chalk | picocolors | tuiuiu.js/colors |
|---|---|---|---|
| Zero dependencies | ❌ | ✅ | ✅ |
| Chainable API | ✅ | ❌ | ✅ |
| Template literals | ❌ | ❌ | ✅ |
| Composable styles | ❌ | ❌ | ✅ |
| Background colors | ✅ | ✅ | ✅ |
| Tailwind palette | ❌ | ❌ | ✅ |
| Hex/RGB support | ✅ | ❌ | ✅ |
Chainable API:
import { c } from 'tuiuiu.js/colors'
import { createCLI, type Formatter } from 'cli-args-parser'
const theme: Formatter = {
'section-header': s => c.bold.white(s),
'program-name': s => c.magenta.bold(s),
'version': s => c.green(s),
'command-name': s => c.yellow(s),
// Command options
'option-flag': s => c.green(s),
'option-type': s => c.cyan(s),
'option-default': s => c.dim(s),
// Global options
'global-option-flag': s => c.green(s),
'global-option-type': s => c.cyan(s),
'global-option-default': s => c.dim(s),
'error-message': s => c.red(s),
}
const cli = createCLI({
name: 'api',
version: '1.0.0',
formatter: theme
})Background Colors for Status Badges:
import { c } from 'tuiuiu.js/colors'
const formatter: Formatter = {
// Status badges with background
'error-header': s => c.bgRed.white.bold(` ${s} `),
'warning-header': s => c.bgYellow.black.bold(` ${s} `),
'success-header': s => c.bgGreen.black.bold(` ${s} `),
// Environment indicators
'env-production': s => c.bgRed.white.bold(` PROD `),
'env-staging': s => c.bgYellow.black(` STG `),
'env-development': s => c.bgGreen.black(` DEV `),
// Highlight dangerous commands
'command-name': s => s.includes('delete')
? c.bgRed.white.bold(s) // Dangerous commands in red
: c.yellow(s),
}Template Literals for Complex Output:
import { tpl } from 'tuiuiu.js/colors'
// Instead of manual concatenation
console.log(tpl`{red Error:} File {bold ${filename}} not found`)
console.log(tpl`{bgRed.white DANGER } This action is {bold.red irreversible}`)
console.log(tpl`{green ✓} Deployed to {bgBlue.white ${env} } successfully`)Composable Styles for Reusable Themes:
import { compose, red, bold, bgWhite, black, bgRed, white } from 'tuiuiu.js/colors'
// Pre-composed reusable styles
const errorStyle = compose(red, bold)
const highlightStyle = compose(bgWhite, black, bold)
const dangerBadge = compose(bgRed, white, bold)
const formatter: Formatter = {
'error-message': s => errorStyle(s),
'highlighted-text': s => highlightStyle(` ${s} `),
'danger-badge': s => dangerBadge(` ${s} `),
}Recker Orange Theme
A professional theme inspired by Recker using orange as the primary color with multiple shades. Zero dependencies — pure ANSI escape codes:
import { createCLI, type Formatter } from 'cli-args-parser'
/**
* 🎨 Recker-inspired Orange Theme
*
* Uses ANSI 256 color codes for rich orange palette:
* - 208: Bright orange (#FF8700) — primary
* - 214: Light orange (#FFAF00) — accent
* - 202: Dark orange (#FF5F00) — emphasis
* - 166: Burnt orange (#D75F00) — subtle
* - 252: Light gray (#D0D0D0) — text
* - 245: Medium gray (#8A8A8A) — muted
*/
const ansi = {
// Styles
bold: (s: string) => `\x1b[1m${s}\x1b[22m`,
dim: (s: string) => `\x1b[2m${s}\x1b[22m`,
// Orange palette (ANSI 256)
orange: (s: string) => `\x1b[38;5;208m${s}\x1b[39m`, // Primary
lightOrange: (s: string) => `\x1b[38;5;214m${s}\x1b[39m`, // Accent
darkOrange: (s: string) => `\x1b[38;5;202m${s}\x1b[39m`, // Emphasis
burntOrange: (s: string) => `\x1b[38;5;166m${s}\x1b[39m`, // Subtle
// Neutrals
white: (s: string) => `\x1b[97m${s}\x1b[39m`,
gray: (s: string) => `\x1b[38;5;245m${s}\x1b[39m`,
lightGray: (s: string) => `\x1b[38;5;252m${s}\x1b[39m`,
// Semantic
error: (s: string) => `\x1b[31m${s}\x1b[39m`,
}
// Compose styles
const bold = ansi.bold
const orange = (s: string) => ansi.bold(ansi.orange(s))
const reckerTheme: Formatter = {
// Headers & structure
'section-header': s => bold(ansi.white(s)),
// Identity
'program-name': s => orange(s),
'version': s => ansi.lightOrange(s),
'description': s => ansi.lightGray(s),
// Commands
'command-name': s => ansi.orange(s),
'command-alias': s => ansi.gray(s),
'command-description': s => ansi.lightGray(s),
// Options
'option-flag': s => ansi.lightOrange(s),
'option-type': s => ansi.burntOrange(s),
'option-default': s => ansi.dim(ansi.gray(s)),
'option-description': s => ansi.lightGray(s),
// Positionals
'positional-name': s => ansi.darkOrange(s),
// Errors
'error-header': s => bold(ansi.error(s)),
'error-message': s => ansi.error(s),
'error-option': s => ansi.orange(s),
}
const cli = createCLI({
name: 'rek',
version: '1.0.0',
description: 'HTTP client for the AI era',
commands: {
get: { description: 'Make a GET request' },
post: { description: 'Make a POST request' },
ai: { description: 'AI-powered requests', aliases: ['chat'] }
},
options: {
output: { short: 'o', type: 'string', description: 'Output file' },
verbose: { short: 'v', type: 'boolean', description: 'Verbose output' },
timeout: { short: 't', type: 'number', default: 30000, description: 'Request timeout (ms)' }
},
formatter: reckerTheme
})
console.log(cli.help())Output Preview:
rek 1.0.0 ← orange bold + light orange
HTTP client for the AI era ← light gray
Usage: rek [command] [options]
Commands:
get Make a GET request ← orange + light gray
post Make a POST request
ai AI-powered requests
Global Options:
-o, --output <string> Output file ← light orange + burnt orange
-v, --verbose Verbose output
-t, --timeout <number> Request timeout (ms) (default: 30000)💡 Tip: Copy the
ansiobject to your project as a zero-dependency color utility. It works in any terminal that supports ANSI 256 colors (most modern terminals).
Raw Tokens
For advanced use cases (generating Markdown, HTML, JSON), access raw tokens directly:
import { createCLI, generateCLIHelpTokens, generateVersionTokens } from 'cli-args-parser'
const cli = createCLI({
name: 'myapp',
version: '1.0.0',
commands: {
deploy: { description: 'Deploy the app' }
}
})
// Get structured tokens
const helpTokens = generateCLIHelpTokens(cli.schema)
const versionTokens = generateVersionTokens(cli.schema)
// Example: Convert to Markdown
function toMarkdown(tokens) {
return tokens.map(({ type, value }) => {
switch (type) {
case 'section-header': return `\n## ${value}\n`
case 'program-name': return `**${value}**`
case 'command-name': return `\`${value.trim()}\``
case 'option-flag': return `\`${value}\``
default: return value
}
}).join('')
}
// Example: Extract data
const commands = helpTokens
.filter(t => t.type === 'command-name')
.map(t => t.value.trim())
// ['deploy']Token Types
Help Tokens (23 types):
| Token | Description | Example Value |
|---|---|---|
section-header |
Section titles | "Usage:", "Options:" |
program-name |
CLI name | "myapp" |
version |
Version number | "1.0.0" |
description |
CLI/command description | "My awesome CLI" |
command-name |
Command name | "deploy" |
command-alias |
Command aliases | "(aliases: d, dep)" |
command-description |
Command description | "Deploy to production" |
option-flag |
Command option flags | "-v, --verbose" |
option-type |
Command option type hint | "<string>" |
option-default |
Command option default | "(default: 3000)" |
option-description |
Command option description | "Enable verbose output" |
global-option-flag |
Global option flags | "-v, --verbose" |
global-option-type |
Global option type hint | "<string>" |
global-option-default |
Global option default | "(default: 3000)" |
global-option-description |
Global option description | "Enable verbose output" |
positional-name |
Positional arg name | "<file>" |
positional-optional |
Optional marker | " (optional)" |
usage-text |
Usage pattern | "[options] <file>" |
error-message |
Error text | "Unknown command: xyz" |
text |
Plain text/spacing | " " |
newline |
Line break | "\n" |
indent |
Indentation | " " |
💡 Global vs Command Options: Global options (defined at CLI root) use
global-option-*tokens. Command-specific options useoption-*tokens. This allows different visual styling for each.
Error Tokens (7 types):
| Token | Description |
|---|---|
error-header |
Error section header |
error-bullet |
Bullet point marker |
error-message |
Error message text |
error-option |
Option name in error |
error-value |
Invalid value in error |
error-command |
Command name in error |
text |
Plain text |
Global vs Command Options
Global options are defined at the CLI root level and apply to all commands. Command options are specific to each command.
const cli = createCLI({
name: 'deploy',
// Global options - available to all commands
options: {
verbose: { short: 'v', type: 'boolean', description: 'Verbose output' },
config: { short: 'c', type: 'string', description: 'Config file' }
},
commands: {
prod: {
description: 'Deploy to production',
// Command-specific options
options: {
force: { short: 'f', type: 'boolean', description: 'Force deployment' }
}
}
}
})Token Differentiation:
In help output, global options use global-option-* tokens while command options use option-* tokens. This allows different visual styling:
import { c } from 'tuiuiu.js/colors'
const cli = createCLI({
name: 'deploy',
options: {
verbose: { short: 'v', type: 'boolean', description: 'Verbose output' }
},
commands: {
prod: {
description: 'Deploy to production',
options: {
force: { short: 'f', type: 'boolean', description: 'Force deployment' }
}
}
},
formatter: {
// Command options in green
'option-flag': s => c.green(s),
'option-description': s => c.white(s),
// Global options in cyan (dimmer)
'global-option-flag': s => c.cyan(s),
'global-option-description': s => c.dim(s),
},
help: {
includeGlobalOptionsInCommands: true // Show global options in command help
}
})Including Global Options in Command Help:
By default, command help only shows command-specific options. Enable help.includeGlobalOptionsInCommands to also show global options:
# Without includeGlobalOptionsInCommands (default)
$ deploy prod --help
deploy prod
Deploy to production
Usage: deploy prod [options]
Options:
-f, --force Force deployment
# With includeGlobalOptionsInCommands: true
$ deploy prod --help
deploy prod
Deploy to production
Usage: deploy prod [options]
Options:
-f, --force Force deployment
Global Options:
-v, --verbose Verbose output
-c, --config Config fileAdditional Features
Environment Variable Fallback
const parser = createParser({
options: {
apiKey: {
type: 'string',
env: 'API_KEY', // Falls back to $API_KEY
required: true
},
debug: {
type: 'boolean',
env: 'DEBUG',
default: false
}
}
})Priority: CLI arg > Environment variable > Default value
Variadic Positionals
Capture all remaining arguments:
const parser = createParser({
positional: [
{ name: 'command', required: true },
{ name: 'files', variadic: true }
]
})
parser.parse(['build', 'src/a.ts', 'src/b.ts', 'src/c.ts'])
// positional: { command: 'build', files: ['src/a.ts', 'src/b.ts', 'src/c.ts'] }Strict Mode
Reject unknown options:
const parser = createParser({
strict: true,
options: { verbose: { type: 'boolean' } }
})
parser.parse(['--unknown'])
// errors: ['Unknown option: --unknown']Negation with Aliases
Negation works with aliases and normalizes to the canonical option name:
const parser = createParser({
options: {
verbose: {
type: 'boolean',
default: true,
aliases: ['debug']
}
}
})
parser.parse(['--no-debug'])
// options: { verbose: false }
// 'debug' is not set — normalized to canonical name 'verbose'API Reference
parse(args, options?)
Basic parsing without schema validation.
import { parse } from 'cli-args-parser'
const result = parse(process.argv.slice(2), {
separators: { /* custom separators */ },
strict: false,
stopEarly: false,
excludePatterns: [/^https?:\/\//]
})Returns: ParsedArgs
interface ParsedArgs {
positional: string[]
flags: Record<string, boolean>
options: Record<string, PrimitiveValue>
errors: string[]
// Dynamic categories based on separators:
// Category value type depends on separator config:
// - typed: false (default) → string
// - typed: true → TypedValue (string | number | boolean | null | array | object)
data: Record<string, TypedValue> // ':=' is typed by default
meta: Record<string, string> // ':' is not typed by default
// Note: if you configure ':' with { typed: true }, meta would be Record<string, TypedValue>
}
type PrimitiveValue = string | number | boolean | null
type TypedValue = PrimitiveValue | TypedValue[] | { [key: string]: TypedValue }createEmptyResult(separators?)
Create an empty result object with categories initialized.
import { createEmptyResult, DEFAULT_SEPARATORS } from 'cli-args-parser'
const empty = createEmptyResult()
// { positional: [], flags: {}, options: {}, errors: [], data: {}, meta: {} }
const custom = createEmptyResult({ '=': 'params', '@': 'files' })
// { positional: [], flags: {}, options: {}, errors: [], params: {}, files: {} }mergeResults(base, overlay)
Merge two parse results, with overlay values taking precedence.
import { parse, mergeResults } from 'cli-args-parser'
const defaults = parse(['name=default', '--verbose'])
const userArgs = parse(['name=custom', 'age:=30'])
const merged = mergeResults(defaults, userArgs)
// data: { name: 'custom', age: 30 }, flags: { verbose: true }createParser(schema)
Create a parser with validation, defaults, and help generation.
import { createParser } from 'cli-args-parser'
const parser = createParser({
positional: [/* ... */],
options: { /* ... */ },
separators: { /* ... */ },
strict: false,
stopEarly: false,
allowUnknown: true,
autoShort: false,
formatter: { /* token styling */ }
})
parser.parse(args) // Parse arguments
parser.help() // Generate help text
parser.schema // Access schemaReturns: Parser
interface Parser {
parse(args: string[]): SchemaParseResult
help(): string
schema: ParserSchema
}createCLI(schema)
Create a CLI with subcommands, handlers, and completion.
import { createCLI } from 'cli-args-parser'
const cli = createCLI({
name: 'myapp',
version: '1.0.0',
description: 'My awesome CLI',
options: { /* global options */ },
commands: { /* ... */ },
separators: { /* ... */ },
strict: false,
autoShort: false,
formatter: { /* token styling */ },
config: {
files: ['.myapprc', 'myapp.config.json'],
searchPlaces: ['cwd', 'home']
}
})
cli.parse(args) // Parse arguments
await cli.run(args) // Parse and execute handler
cli.version() // Version string (respects formatter)
cli.help(['config']) // Help for specific command
cli.completion('bash') // Shell completion script
cli.schema // Access schemaReturns: CLI
interface CLI {
parse(args: string[]): CommandParseResult
run(args: string[]): Promise<void>
version(): string
help(command?: string[]): string
completion(shell: 'bash' | 'zsh' | 'fish'): string
schema: CLISchema
}Exported Types
import type {
// Core types
PrimitiveValue,
ParsedArgs,
Token,
TokenType,
Separators,
ParserOptions,
// Schema types
OptionType,
OptionDefinition,
PositionalDefinition,
ParserSchema,
SchemaParseResult,
Parser,
ValidateFn,
ValidationResult,
// CLI types
CommandDefinition,
CLISchema,
ConfigOptions,
CommandParseResult,
CLI,
// Internal types
ResolvedOption,
// Formatter types
Formatter,
FormatterFn,
HelpToken,
HelpTokenType,
ErrorToken,
ErrorTokenType
} from 'cli-args-parser'Utility Functions
import {
// Tokenizer
tokenize,
looksLikeValue,
groupTokens,
// Coercion
coercePrimitive,
coerceTyped,
coerceToType,
coerceToBoolean,
coerceToNumber,
stripQuotes,
isNumericString,
isBooleanString,
inferType,
// Help & Version
generateHelp,
generateCLIHelp,
generateHelpTokens,
generateCLIHelpTokens,
generateVersion,
generateVersionTokens,
wrapText,
// Formatter
renderTokens,
token,
tokens,
shouldUseColor,
getEffectiveFormatter,
// Completion
generateCompletion,
// Errors
ParseError,
MissingRequiredError,
MissingPositionalError,
InvalidValueError,
UnknownOptionError,
TypeCoercionError,
UnknownCommandError,
formatErrors,
formatErrorsTokens,
createErrorMessage,
createErrorTokens,
hasErrors,
throwIfErrors,
// Constants
DEFAULT_SEPARATORS,
URL_PROTOCOLS
} from 'cli-args-parser'License
MIT