CLI tool
tj/commander.js avatar
tj/commander.js

Commander.js: strict flags, generated help, and a Node 22.12.0 floor

node.js command-line interfaces made easy

28,453 stars1,797 forksJavaScriptMIT

At a glance

What is it?
A close look at TJ Holowaychuk's MIT licensed Node.js argument parser: how the strict unknown-option error works, what the option declarations feed into help output, and what the manifest and its scripts commit to.
Who is it for?
Commander.js fits a Node project that wants subcommands, generated help and strict option errors without writing a parser of its own, and the cost is a specific runtime floor plus a narrow published surface. Before adopting it, check three things: whether your runtime clears Node 22.12.0, whether the single `.` export blocks deep imports you already rely on, and whether the four published paths cover anything you planned to patch.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 8 days ago.
What is it written in?
Mainly JavaScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on November 14, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • tj/commander.js28,453 ★ · +41
Star history
Repository2026-09-282026-11-14
tj/commander.js28.4k28.5k

41 Stars in the last 47 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

A shared program object, or a Command built in your own file

The package exports a `program` object that is usable with no setup, which is why several walkthrough snippets reach for it. For brevity it is imported on a single line:

js
import { program } from 'commander';

For anything larger, and for unit tests that need a parser with fresh state, the guidance is to construct a local instance instead. The same two lines appear once per module system, so the file extension decides the syntax:

js
// CommonJS (.cjs)
const { Command } = require('commander');
const program = new Command();
js
// ECMAScript (.mjs)
import { Command } from 'commander';
const program = new Command();

TypeScript files use the import from the ECMAScript version. The distinction is not cosmetic: `program` is one instance shared by the whole process, while `new Command()` hands each caller its own parser.

Unknown options stop the run and the error names the nearest flag

Parsing is strict, and a flag that was never declared aborts the run with an error that can name the option you probably meant:

console
$ node split.js -s - --fits a-b-c
error: unknown option '--fits'
(Did you mean --first?)
$ node split.js -s - --first a-b-c
[ 'a' ]

A double dash marks the end of option processing, which is the escape hatch for a value that begins with a hyphen. Option names longer than one word are normalised: `--template-engine` surfaces as the `templateEngine` property on the object returned by `.opts()`, and that object is what an action handler receives.

A value reaches its flag in four positions and a flag pair takes two long names

An option declared as `-p, --port <number>` accepts its value in any of four positions, and all four are the same command line:

sh
serve -p 80
serve -p80
serve --port 80
serve --port=80

The short form takes the value directly or after a space; the long form takes it after a space or after an equals sign. A flag pair may be separated by a comma, a space, or a vertical bar, and it need not contain a single character: `--ws, --workspace <name>` gives one option a short-ish alias that is itself a long name. `.option()` doubles as its own documentation, since the flag string is what reappears in help output. A third argument supplies a default, so `-s, --separator <char>` can carry `','`, and the help line then prints that default.

Help text is assembled from the same declarations the parser uses

Ask a subcommand for help and the text is built from what was declared, argument descriptions and option descriptions alike:

console
$ node string-util.js help split
Usage: string-util split [options] <string>

Split a string into substrings and display as an array.

Arguments:
  string                  string to split

Options:
  --first                 display just the first substring
  -s, --separator <char>  separator character (default: ",")
  -h, --help              display help for command

$ node string-util.js split --separator=- a-b-c
[ 'a', 'b', 'c' ]

`help split` is the explicit spelling of the `-h, --help` line that closes every command. The rest is settable piece by piece: `.name()` and `.usage()` control the usage line, `.description()` and `.summary()` control the sentence under it, `.helpOption(flags, description)` replaces the help flag itself, and `.helpCommand()` decides whether a `help` subcommand exists at all. A Help Groups feature covers sorting options into named blocks instead of one flat list.

The published package is four file globs behind one entry point

The manifest ships much less than the repository tree suggests. Version 15.0.0 publishes `index.js`, `lib/*.js`, `typings/index.d.ts` and `package-support.json`, and nothing else, which leaves `examples/`, `tests/` and `docs/` on the development side of the boundary. `type` is `module` and `main` points at `./index.js`, while `exports` declares a single `.` key with `types` and `default`, so there is no subpath to import from; the same types path repeats in the `types` field. Installing it takes one command:

sh
npm install commander

The engine floor is stated outright as `node >=22.12.0`. That excludes the older runtime lines a great many command-line tools still run on, so a project pinned to an earlier Node stays on an earlier Commander. The `support` field is set to true, and the keywords are commander, command, option, parser, cli, argument, args and argv.

Three tsconfig files keep the JavaScript and TypeScript checks apart

`tsconfig.json`, `tsconfig.js.json` and `tsconfig.ts.json` sit side by side at the top level, and the scripts do not merge them. `check:type:js` runs `tsc -p tsconfig.js.json` over the library's own JavaScript, while `check:type:ts` runs `tsd` and then `tsc -p tsconfig.ts.json`, so the hand-written declarations in `typings/` are exercised by type tests instead of being inferred from the source. `check` chains the type check, `eslint .` and `prettier --check .`. `test` runs `node --test` followed by the TypeScript check, and `test-all` adds lint and format on top of both, with `fix` providing the write variants. The pinned toolchain is high: eslint `^10.0.2`, typescript `^6.0.2`, tsd `^0.33.0`, prettier `^3.2.5` and `@types/node` `^22.7.4`.

The examples folder keeps the awkward cases next to the working ones

The walkthrough leans on `split.js` and `string-util.js`, but the `examples/` directory holds far more. Alongside those two sit `alias.cjs`, `argument.js`, `arguments-extra.js`, `arguments-custom-processing.js`, `defaultCommand.js`, `nestedCommands.js`, `deploy`, `description`, `hook.js`, `custom-command-class.js`, `global-options-added.js` and `global-options-nested.js`. Help output has its own set: `color-help.mjs`, `color-help-replacement.mjs`, `man-style-help.mjs`, `help-centered.mjs`, `help-groups.js`, `help-subcommands-usage.js` and a `custom-help` directory, plus `configure-output.js`. Sections on overriding exit and output handling and on displaying errors cover the failure paths. None of these files are published, so they are read from the repository rather than from `node_modules`.

Three releases since January and a push on 2026-09-28

The release list is short. v14.0.3 shipped on 2026-01-31, a prerelease v15.0.0-0 followed on 2026-02-21, and v15.0.0 arrived on 2026-05-29, which is the version string the manifest carries. The repository is not archived, its default branch is `master`, and the last push was on 2026-11-01. Not everything lives in one file: `Readme.md` and `Readme_zh-CN.md` sit side by side so Chinese readers get their own copy of the walkthrough, and the vocabulary used throughout has its own page at `docs/terminology.md`. `CHANGELOG.md`, `CONTRIBUTING.md`, `SECURITY.md`, `eslint.config.js`, `.prettierrc.js` and `.prettierignore` cover the rest, and `package-support.json` is one of the four paths that reach a user.

Editorial conclusion

Commander.js fits a Node project that wants subcommands, generated help and strict option errors without writing a parser of its own, and the cost is a specific runtime floor plus a narrow published surface. Before adopting it, check three things: whether your runtime clears Node 22.12.0, whether the single `.` export blocks deep imports you already rely on, and whether the four published paths cover anything you planned to patch. A project that fails one of those checks belongs on an older tag or on a different parser.

Frequently asked questions

What is Commander.js?

Commander.js is a Node.js library for building command-line interfaces: you write code declaring options, arguments and subcommands, and it parses the arguments into options and command-arguments, displays usage errors, and implements a help system. Version 15.0.0 is MIT licensed, authored by TJ Holowaychuk, and requires Node 22.12.0 or newer.

How do I use Commander.js?

Install it with `npm install commander`, then import `program` from `commander` for a quick program or construct `new Command()` for a larger one, declare options with `.option()`, and finish with `program.parse()`. Values come back from `program.opts()`, and each declaration is also what appears in the generated help.

Does Commander.js reject flags it does not know?

Yes. It is strict and prints an error for unrecognised options, and the message can name the closest declared flag, as in `error: unknown option '--fits'` followed by `(Did you mean --first?)`.

Which Node.js versions does Commander.js 15 support?

The manifest sets engines to `node >=22.12.0`, sets `type` to `module` and points `main` at `./index.js`, so a project on an older runtime line needs an earlier Commander release.

Official sources

  1. Issues
  2. License: MIT
  3. README
  4. Releases
  5. tj/commander.js on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/tj-commander-js.svg)](https://hysenlabs.com/projects/tj-commander-js)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/tj-commander-js.svg)](https://hysenlabs.com/projects/tj-commander-js)

Was this useful?