# cmd-ts

> > 💻 A type-driven command line argument parser, with awesome error reporting 🤤

Latest version **0.15.0** (published 2026-02-12) · MIT license · 0 weekly downloads

## Install

```sh
npm install cmd-ts
pnpm add cmd-ts
yarn add cmd-ts
bun add cmd-ts
```

## Health

**Score 65/100 (B)** — status: stable.

Positive: has types; esm support; no vulnerabilities; has provenance; high quality score.

Warnings: low downloads; pre 1.0.

## Facts

| | |
|---|---|
| Version | 0.15.0 |
| Published | 2026-02-12 |
| First published | 2020-03-12 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | bundled |
| Module format | ESM + CommonJS |
| Dependencies | 4 |
| Unpacked size | 397.9 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| Provenance | attested (GitHub Actions) |
| GitHub stars | 365 |
| Author | Gal Schlezinger |
| Maintainers | schlez |

## Links

- npm: https://www.npmjs.com/package/cmd-ts
- Repository: https://github.com/Schniz/cmd-ts
- Homepage: https://cmd-ts.now.sh
- Issues: https://github.com/Schniz/cmd-ts/issues
- npm.io page: https://npm.io/package/cmd-ts

## Dependencies (4)

- [chalk](https://npm.io/package/chalk.md) ^5.4.1
- [debug](https://npm.io/package/debug.md) ^4.4.1
- [didyoumean](https://npm.io/package/didyoumean.md) ^1.2.2
- [strip-ansi](https://npm.io/package/strip-ansi.md) ^7.1.0

## Recent versions

- 0.15.0 (latest) — 2026-02-12
- 0.14.3 — 2025-10-15
- 0.14.2 — 2025-09-21
- 0.14.1 — 2025-08-24
- 0.13.0 — 2023-07-17
- 0.12.1 — 2023-02-13
- 0.12.0 — 2023-01-20
- 0.11.0 — 2022-05-17
- 0.10.2 — 2022-05-17
- 0.10.1 — 2022-04-04
- 0.10.0 — 2022-02-10
- 0.9.0 — 2021-12-26
- 0.8.0 — 2021-11-29
- 0.7.0 — 2021-05-24
- 0.6.9 — 2021-04-18
- … 19 more at https://npm.io/package/cmd-ts/versions

## README

# `cmd-ts`

> 💻 A type-driven command line argument parser, with awesome error reporting 🤤

Not all command line arguments are strings, but for some reason, our CLI parsers force us to use strings everywhere. 🤔 `cmd-ts` is a fully-fledged command line argument parser, influenced by Rust's [`clap`](https://github.com/clap-rs/clap) and [`structopt`](https://github.com/TeXitoi/structopt):

🤩 Awesome autocomplete, awesome safeness

🎭 Decode your own custom types from strings with logic and context-aware error handling

🌲 Nested subcommands, composable API

### Basic usage

```ts
import { command, run, string, number, positional, option } from 'cmd-ts';

const cmd = command({
  name: 'my-command',
  description: 'print something to the screen',
  version: '1.0.0',
  args: {
    number: positional({ type: number, displayName: 'num' }),
    message: option({
      long: 'greeting',
      type: string,
    }),
  },
  handler: (args) => {
    args.message; // string
    args.number; // number
    console.log(args);
  },
});

run(cmd, process.argv.slice(2));
```

#### `command(arguments)`

Creates a CLI command.

### Decoding custom types from strings

Not all command line arguments are strings. You sometimes want integers, UUIDs, file paths, directories, globs...

> **Note:** this section describes the `ReadStream` type, implemented in `./src/example/test-types.ts`

Let's say we're about to write a `cat` clone. We want to accept a file to read into stdout. A simple example would be something like:

```ts
// my-app.ts

import { command, run, positional, string } from 'cmd-ts';

const app = command({
  /// name: ...,
  args: {
    file: positional({ type: string, displayName: 'file' }),
  },
  handler: ({ file }) => {
    // read the file to the screen
    fs.createReadStream(file).pipe(stdout);
  },
});

// parse arguments
run(app, process.argv.slice(2));
```

That works okay. But we can do better. In which ways?

- Error handling is out of the command line argument parser context, and in userland, making things less consistent and pretty.
- It shows we lack composability and encapsulation — and we miss a way to distribute shared "command line" behavior.

What if we had a way to get a `Stream` out of the parser, instead of a plain string? This is where `cmd-ts` gets its power from, custom type decoding:

```ts
// ReadStream.ts

import { Type } from 'cmd-ts';
import fs from 'fs';

// Type<string, Stream> reads as "A type from `string` to `Stream`"
const ReadStream: Type<string, Stream> = {
  async from(str) {
    if (!fs.existsSync(str)) {
      // Here is our error handling!
      throw new Error('File not found');
    }

    return fs.createReadStream(str);
  },
};
```

Now we can use (and share) this type and always get a `Stream`, instead of carrying the implementation detail around:

```ts
// my-app.ts

import { command, run, positional } from 'cmd-ts';

const app = command({
  // name: ...,
  args: {
    stream: positional({ type: ReadStream, displayName: 'file' }),
  },
  handler: ({ stream }) => stream.pipe(process.stdout),
});

// parse arguments
run(app, process.argv.slice(2));
```

Encapsulating runtime behaviour and safe type conversions can help us with awesome user experience:

- We can throw an error when the file is not found
- We can try to parse the string as a URI and check if the protocol is HTTP, if so - make an HTTP request and return the body stream
- We can see if the string is `-`, and when it happens, return `process.stdin` like many Unix applications

And the best thing about it — everything is encapsulated to an easily tested type definition, which can be easily shared and reused. Take a look at [io-ts-types](https://github.com/gcanti/io-ts-types), for instance, which has types like DateFromISOString, NumberFromString and more, which is something we can totally do.

## Inspiration

This project was previously called `clio-ts`, because it was based on `io-ts`. This is no longer the case, because I want to reduce the dependency count and mental overhead. I might have a function to migrate types between the two.

---
_Source: https://npm.io/package/cmd-ts · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
