New Research: Supply Chain Attack on Axios Pulls Malicious Dependency from npm.Details →
Socket
Book a DemoSign in
Socket

cli-args-parser

Package Overview
Dependencies
Maintainers
1
Versions
20
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

cli-args-parser

Expressive CLI argument parser with schema validation

latest
Source
npmnpm
Version
1.0.6
Version published
Weekly downloads
2.3K
238.24%
Maintainers
1
Weekly downloads
 
Created
Source

cli-args-parser

Expressive CLI Argument Parser

Expressive syntax meets powerful schema validation.
Zero dependenciesTypeScript-firstNested subcommandsCustom validation
Parse key=value, key:=typed, Key:Meta — with fully customizable separators.

npm version npm downloads TypeScript Node.js License Zero Dependencies

Quick Start · Syntax · Schema · Validation · CLI · API

Quick Start

npm install cli-args-parser
# or
pnpm add cli-args-parser
import { 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

CategoryFeatures
SyntaxExpressive key=value, key:=typed, Key:Value patterns
SeparatorsFully customizable — define your own patterns and categories
SchemaType coercion, required fields, choices, defaults, env vars
ValidationBuilt-in + custom validation functions
CommandsNested subcommands with unlimited depth
OptionsAuto-short generation, aliases, negation (--no-flag)
OutputShell completion (Bash, Zsh, Fish), help generation
FormattingToken-based theming, bring your own colors (chalk, picocolors, tuiuiu.js/colors, ANSI)
Quality431 tests, zero dependencies, TypeScript-first

Syntax Reference

Default Separators

SyntaxCategoryExampleResult
key=valuedataname=Filipedata.name = "Filipe"
key:=valuedata (typed)age:=35data.age = 35
Key:ValuemetaAuth:Bearer Xmeta.Auth = "Bearer X"
--flagflags--verboseflags.verbose = true
--no-flagflags*--no-colorflags.color = false
-fflags-vflags.v = true
--opt=valoptions--output=fileoptions.output = "file"
-o valoptions-o fileoptions.o = "file"
--marker-- --helpEnd of options, rest is positional

Notes:

  • The Key:Value meta syntax requires the key to start with an uppercase letter (e.g., Authorization:Bearer). Lowercase keys like nginx:latest are 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/file instead of path/to=file. Namespaced keys like aws:region=value work because they don't contain /.
  • * --no-flag behavior differs between APIs:
    • parse(): Always goes to flags as flags.flag = false
    • createParser()/createCLI(): Goes to options as options.flag = false (only for defined boolean options with negatable !== 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 parsed

This 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 rest

Note: createCLI() uses strict instead of allowUnknown. With strict: true, unknown options cause errors, but extras after -- always go to rest (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:

FormatDescriptionExample
stringCategory 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) => ValidationResult

Validation Behavior:

  • Runs after type coercion
  • Only runs if value is defined (not on undefined)
  • Combines with choices validation (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'])   // OK

Auto-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 (verbosev)
  • If taken, try uppercase (verboseV)
  • If taken, try next letter (verbosee)

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 lodash

Validation 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 -V

Output:

myapp 1.0.0
My awesome CLI

You 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 --help

Priority (highest to lowest):

  • --no-color / --color CLI flags
  • FORCE_COLOR environment variable
  • NO_COLOR environment 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 set

Using 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 version

Using 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:

Featurechalkpicocolorstuiuiu.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 ansi object 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):

TokenDescriptionExample Value
section-headerSection titles"Usage:", "Options:"
program-nameCLI name"myapp"
versionVersion number"1.0.0"
descriptionCLI/command description"My awesome CLI"
command-nameCommand name"deploy"
command-aliasCommand aliases"(aliases: d, dep)"
command-descriptionCommand description"Deploy to production"
option-flagCommand option flags"-v, --verbose"
option-typeCommand option type hint"<string>"
option-defaultCommand option default"(default: 3000)"
option-descriptionCommand option description"Enable verbose output"
global-option-flagGlobal option flags"-v, --verbose"
global-option-typeGlobal option type hint"<string>"
global-option-defaultGlobal option default"(default: 3000)"
global-option-descriptionGlobal option description"Enable verbose output"
positional-namePositional arg name"<file>"
positional-optionalOptional marker" (optional)"
usage-textUsage pattern"[options] <file>"
error-messageError text"Unknown command: xyz"
textPlain text/spacing" "
newlineLine break"\n"
indentIndentation" "

💡 Global vs Command Options: Global options (defined at CLI root) use global-option-* tokens. Command-specific options use option-* tokens. This allows different visual styling for each.

Error Tokens (7 types):

TokenDescription
error-headerError section header
error-bulletBullet point marker
error-messageError message text
error-optionOption name in error
error-valueInvalid value in error
error-commandCommand name in error
textPlain 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 file

Additional 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 schema

Returns: 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 schema

Returns: 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

Keywords

cli

FAQs

Package last updated on 20 Jan 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts