MCPcopy Index your code
hub / github.com/privatenumber/cleye

github.com/privatenumber/cleye @v2.6.0

Chat with this repo
repository ↗ · DeepWiki ↗ · release v2.6.0 ↗ · + Follow
58 symbols 179 edges 31 files 0 documented · 0% 8 cross-repo links updated 11d agov2.6.0 · 2026-04-18★ 6746 open issues
What it actually does AI analysis from the code graph — generated when you open this
loading…
README
<img width="110" src="https://github.com/privatenumber/cleye/raw/v2.6.0/github/logo.webp">

cleye

The intuitive command-line interface (CLI) development tool.

Features

  • Minimal API surface
  • Powerful flag parsing
  • Strongly typed parameters and flags
  • Command support
  • Help documentation generation (customizable too!)

Try it out online

<a href="https://github.com/sponsors/privatenumber/sponsorships?tier_id=398771"><img width="412" src="https://raw.githubusercontent.com/privatenumber/sponsors/master/banners/assets/donate.webp"></a>
<a href="https://github.com/sponsors/privatenumber/sponsorships?tier_id=397608"><img width="412" src="https://raw.githubusercontent.com/privatenumber/sponsors/master/banners/assets/sponsor.webp"></a>

Already a sponsor? Join the discussion in the Development repo!

Install

npm i cleye

About

Cleye makes it very easy to develop command-line scripts in Node.js. It handles argv parsing to give you strongly typed parameters + flags and generates --help documentation based on the provided information.

Here's an example script that simply logs: Good morning/evening <name>!:

greet.js:

import { cli } from 'cleye'

// Parse argv
const argv = cli({
    name: 'greet.js',

    // Define parameters
    parameters: [
        '<first name>', // First name is required
        '[last name]' // Last name is optional
    ],

    // Define flags/options
    flags: {

        // Parses `--time` as a string
        time: {
            type: String,
            description: 'Time of day to greet (morning or evening)',
            default: 'morning'
        }
    }
})

const name = [argv._.firstName, argv._.lastName].filter(Boolean).join(' ')

if (argv.flags.time === 'morning') {
    console.log(`Good morning ${name}!`)
} else {
    console.log(`Good evening ${name}!`)
}

🛠 In development, type hints are provided on parsed flags and parameters:

<img src="https://github.com/privatenumber/cleye/raw/v2.6.0/github/typed-flags.png" width="600">



<i>Type hints for Cleye's output are very verbose and readable</i>

📖 Generated help documentation can be viewed with the --help flag:

$ node greet.js --help

greet.js

Usage:
  greet.js [flags...] <first name> [last name]

Flags:
  -h, --help                 Show help
      --time <string>        Time of day to greet (morning or evening) (default: "morning")

✅ Run the script to see it in action:

$ node greet.js John Doe --time evening

Good evening John Doe!

Examples

Want to dive right into some code? Check out some of these examples:

Usage

Arguments

Arguments are values passed into the script that are not associated with any flags/options.

For example, in the following command, the first argument is file-a.txt and the second is file-b.txt:

$ my-script file-a.txt file-b.txt

Arguments can be accessed from the _ array-property of the returned object.

Example:

const argv = cli({ /* ... */ })

// $ my-script file-a.txt file-b.txt

argv._ // => ["file-a.txt", "file-b.txt"] (string[])

Parameters

Parameters (aka positional arguments) are the names that map against argument values. Think of parameters as variable names and arguments as values associated with the variables.

Parameters can be defined in the parameters array-property to make specific arguments accessible by name. This is useful for writing more readable code, enforcing validation, and generating help documentation.

Parameters are defined in the following formats: - Required parameters are indicated by angle brackets (eg. <parameter name>). - Optional parameters are indicated by square brackets (eg. [parameter name]). - Spread parameters are indicated by ... suffix (eg. <parameter name...> or [parameter name...]).

Note, required parameters cannot come after optional parameters, and spread parameters must be last.

Parameters can be accessed in camelCase on the _ property of the returned object.

Example:

const argv = cli({
    parameters: [
        '<required parameter>',
        '[optional parameter]',
        '[optional spread...]'
    ]
})

// $ my-script a b c d

argv._.requiredParameter // => "a" (string)
argv._.optionalParameter // => "b" (string | undefined)
argv._.optionalSpread // => ["c", "d"] (string[])

End-of-flags

End-of-flags (--) (aka end-of-options) allows users to pass in a subset of arguments. This is useful for passing in arguments that should be parsed separately from the rest of the arguments or passing in arguments that look like flags.

An example of this is npm run:

$ npm run <script> -- <script arguments>

The -- indicates that all arguments afterwards should be passed into the script rather than npm.

All end-of-flag arguments will be accessible from argv._['--'].

Additionally, you can specify -- in the parameters array to parse end-of-flags arguments.

Example:

const argv = cli({
    name: 'npm-run',
    parameters: [
        '<script>',
        '--',
        '[arguments...]'
    ]
})

// $ npm-run echo -- hello world

argv._.script // => "echo" (string)
argv._.arguments // => ["hello", "world] (string[])

Flags

Flags (aka Options) are key-value pairs passed into the script in the format --flag-name <value>.

For example, in the following command, --file-a has value data.json and --file-b has value file.txt:

$ my-script --file-a data.json --file-b=file.txt

Parsing features

Cleye's flag parsing is powered by type-flag and comes with many features:

  • Array & Custom types
  • Flag delimiters: --flag value, --flag=value, --flag:value, and --flag.value
  • Combined aliases: -abcd 2-a -b -c -d 2
  • End of flags: Pass in -- to end flag parsing
  • Unknown flags: Unexpected flags stored in unknownFlags

Read the type-flag docs to learn more.

Defining flags

Flags can be specified in the flag object-property, where the key is the flag name, and the value is a flag type function or an object that describes the flag.

The flag name is recommended to be in camelCase as it will be interpreted to parse kebab-case equivalents.

The flag type function can be any function that accepts a string and returns the parsed value. Default JavaScript constructors should cover most use-cases: String, Number, Boolean, etc.

The flag description object can be used to store additional information about the flag, such as alias, default, and description. To accept multiple values for a flag, wrap the type function in an array.

All of the provided information will be used to generate better help documentation.

Example:

const argv = cli({
    flags: {
        someBoolean: Boolean,

        someString: {
            type: String,
            description: 'Some string flag',
            default: 'n/a'
        },

        someNumber: {
            // Wrap the type function in an array to allow multiple values
            type: [Number],
            alias: 'n',
            description: 'Array of numbers. (eg. -n 1 -n 2 -n 3)'
        }
    }
})

// $ my-script --some-boolean --some-string hello --some-number 1 -n 2

argv.flags.someBoolean // => true (boolean | undefined)
argv.flags.someString // => "hello" (string)
argv.flags.someNumber // => [1, 2] (number[])

Boolean flag negation

To support --no-<flag> syntax for boolean flags, enable booleanFlagNegation:

cli({
    flags: {
        verbose: Boolean
    },
    booleanFlagNegation: true
})
$ my-script --no-verbose
# argv.flags.verbose => false

Last-wins semantics apply between --flag and --no-flag:

$ my-script --verbose --no-verbose
# argv.flags.verbose => false

$ my-script --no-verbose --verbose
# argv.flags.verbose => true

Only applies to flags defined as Boolean. For non-boolean flags, --no-<flag> is treated as an unknown flag.

Commands inherit booleanFlagNegation from the parent CLI, but can override it.

Inverting boolean flags

Alternatively, a boolean flag can be set to false by passing the value using the = operator:

$ my-script --some-boolean=false

Without =, the false will be parsed as a separate argument:

$ my-script --some-boolean false
# argv.flags.someBoolean => true
# argv._ => ['false']

Custom flag types & validation

Custom flag types can be created to validate flags and narrow types. Simply create a new function that accepts a string and returns the parsed value.

Here's an example with a custom Size type that narrows the flag type to "small" | "medium" | "large":

const possibleSizes = ['small', 'medium', 'large'] as const

type Sizes = typeof possibleSizes[number] // => "small" | "medium" | "large"

// Custom type function
const Size = (size: Sizes) => {
    if (!possibleSizes.includes(size)) {
        throw new Error(`Invalid size: "${size}"`)
    }

    return size
}

const argv = cli({
    flags: {
        size: {
            type: Size,
            description: 'Size of the pizza (small, medium, large)'
        }
    }
})

// $ my-script --size large

argv.flags.size // => "large" ("small" | "medium" | "large")

Composable type helpers

cleye/formats is a tree-shakable subpath that ships ready-made type-function helpers for common flag shapes. Import only what you need.

import {
    oneOf, commaList, integer, float, range, url
} from 'cleye/formats'

cli({
    flags: {
        format: { type: oneOf('json', 'yaml', 'csv') }, // => 'json' | 'yaml' | 'csv'
        tags: { type: commaList(String) }, // => string[]
        port: { type: range(1024, 65_535) }, // => number, validated in range
        count: { type: integer() }, // => number (integer only)
        ratio: { type: float() }, // => number (finite float)
        apiUrl: { type: url() } // => URL object
    }
})
Helper Return type Description
oneOf(...values) Union of the given string literals Throws if the value is not in the list.
commaList(itemType) T[] Splits on ,, trims whitespace, maps each item through itemType.
integer() number Parses a base-10 integer. Throws on floats or non-numeric input.
float() number Parses a finite float. Throws on non-finite or non-numeric input.
range(min, max) (input: string) => number Returns a parser that validates the input parses to a number in [min, max].
url() URL Parses with new URL(). Returns a URL object so callers get .host, .pathname, etc.

Default flags

By default, Cleye will try to handle the --help, -h and --version flags.

Help flag

Handling --help, -h is enabled by default.

To disable it, set help to false. The help documentation can still be manually displayed by calling .showHelp(helpOptions) on the returned object.

Version flag

To enable handling --version, specify the version property.

cli({
    version: '1.2.3'
})
$ my-script --version
1.2.3

The version is also shown in the help documentation. To opt out of handling --version while still showing the version in --help, pass the version into help.version.

[!TIP] Import name, version, and description directly from package.json to avoid keeping them in sync manually: ```ts import { name, version, description } from './package.json' with { type: 'json' }

cli({ name, version, help: { description } }) ```

Strict flags

To reject unknown flags with an error, enable strictFlags:

cli({
    flags: {
        foo: Boolean,
        bar: String
    },
    strictFlags: true
})
$ my-script --baz
Error: Unknown flag: --baz. (Did you mean --bar?)

When enabled, the CLI will exit with an error if any unknown flags are passed. If a similar flag name exists (within 2 edits), it will suggest the closest match.

Commands inherit strictFlags from the parent CLI, but can override it:

command({
    name: 'build',
    strictFlags: false // Disable for this command
})

Commands

Commands allow organizing multiple "scripts" into a single script. An example of this is the npm install command, which is essentially an "install" script inside the "npm" script, adjacent to other commands like npm run.

Defini

Core symbols most depended-on inside this repo

Shape

Function 44
Method 12
Class 2

Languages

TypeScript100%

Modules by API surface

src/render-help/renderers.ts14 symbols
src/cli.ts11 symbols
src/render-help/generate-help.ts10 symbols
src/formats.ts6 symbols
tests/specs/help.ts3 symbols
tests/utils/mock-env-functions.ts2 symbols
tests/specs/flags.ts2 symbols
src/utils/convert-case.ts2 symbols
examples/tsc/index.ts2 symbols
tests/specs/types.ts1 symbols
tests/specs/command.ts1 symbols
src/utils/script-name.ts1 symbols

For agents

$ claude mcp add cleye \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact

Ask about this repo answers extend the page