NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
npm · #11 most downloaded on npm
the complete solution for node.js command-line programs
Last release 4 months ago
29 May 2026
Release timing varies
gaps range from 2 weeks to 7 months
Nearly every release is documented
notes for 59 of the last 60 stable releases
Nothing withdrawn
no release was ever pulled
15 years old
124 releases · first in 2011
Breaking: removed deprecated export of commander/esm.mjs
Commander 15 is ESM only. This is expected to be seamless for ESM consumers, but some CommonJS consumers may hit issues with tooling requiring configuration for ESM-only dependencies. See Migration Tips below.
The release of Commander 15 moves Commander 14 into maintenance. Commander 14 will get security updates for
12 months (to May 2027). For more info see Release Policy.
--no-* option sets default option value to true, default not implicitly set when define both positive and negative option in either order (#2405)require(esm)).node:test test runner (#2463)commander/esm.mjs (#2464)Commander 15 is ESM only, but this does not mean you need to migrate to ESM to use it. Importing ESM from CommonJS is
supported by Node.js, and Bun, and Deno. Hopefully it Just Works for you! However, you may be using a different runtime or
some other part of your setup that may not yet natively support importing ESM from CommonJS, such as your testing framework
or bundler.
If you have problems using Commander 15 in your environment, one option is stay on Commander 14 for now. Commander 14 will
get security updates until May 2027 and things will hopefully improve for your setup in the meantime.
One column per quarter.
(Released as 15.0.0)
(Released as 15.0.0)
clarify typing for deprecated callback parameter to .outputHelp()
improve negative number auto-detection test
improve code readability by using optional chaining
Object.assign() (#2395)string.endsWith() instead of string.slice() (#2396).parseOptions() to process args array in-place (#2409)._concatValue() to ._collectValue() (change code from array.concat() to array.push()) (#2410)support for groups of options and commands in the help using low-level .helpGroup() on Option and Command , and higher -level .optionsGroup() and .com
.helpGroup() on Option and Command, and higher.optionsGroup() and .commandsGroup() which can be used in chaining way to specify group title for following optionparseArg property to Argument class (#2359).configureOutput() now makes copy of settings instead of modifying in-place, fixing side-effects (#2350)Help class adding .formatItemList() and .groupItems() methods (#2328)support a pair of long option flags to allow a memorable shortened flag, like .option('--ws, --workspace')
.option('--ws, --workspace') (#2312)support multiple calls to .parse() with default settings
.parse() with default settings (#2299).saveStateBeforeParse() and .restoreStateBeforeParse() for use by subclasses (#2299)styleTitle() to add color to help using .configureHelp() or Help subclass (#2251).configureOutput() for getOutHasColors(), getErrHasColors(), and stripColor() (#2251)minWidthToWrap (#2251)displayWidth(), boxWrap(), preformatted() et al (#2251)- (#2270).parse() if storeOptionsAsProperties: true (#2299)this in parameters for action handler callback (#2197)Help.wrap() refactored into formatItem() and boxWrap() (#2251)Excess command-arguments
It is now an error for the user to specify more command-arguments than are expected. (allowExcessArguments is now false by default.)
Old code:
program.option('-p, --port <number>', 'port number');
program.action((options) => {
console.log(program.args);
});Now shows an error:
$ node example.js a b c
error: too many arguments. Expected 0 arguments but got 3.You can declare the expected arguments. The help will then be more accurate too. Note that declaring
new arguments will change what is passed to the action handler.
program.option('-p, --port <number>', 'port number');
program.argument('[args...]', 'remote command and arguments'); // expecting zero or more arguments
program.action((args, options) => {
console.log(args);
});Or you could suppress the error, useful for minimising changes in legacy code.
program.option('-p, --port', 'port number');
program.allowExcessArguments();
program.action((options) => {
console.log(program.args);
});.parse() with default settings ([#2299]).saveStateBeforeParse() and .restoreStateBeforeParse() for use by subclasses ([#2299])styleTitle() to add color to help using .configureHelp() or Help subclass ([#2251]).configureOutput() for getOutHasColors(), getErrHasColors(), and stripColor() ([#2251])minWidthToWrap ([#2251])displayWidth(), boxWrap(), preformatted() et al ([#2251])- ([#2270])
.parse() if storeOptionsAsProperties: true ([#2299])this in parameters for action handler callback ([#2197])Help.wrap() refactored into formatItem() and boxWrap() ([#2251])Excess command-arguments
It is now an error for the user to specify more command-arguments than are expected. (allowExcessArguments is now false by default.)
Old code:
program.option('-p, --port <number>', 'port number');
program.action((options) => {
console.log(program.args);
});
Now shows an error:
$ node example.js a b c
error: too many arguments. Expected 0 arguments but got 3.
You can declare the expected arguments. The help will then be more accurate too. Note that declaring new arguments will change what is passed to the action handler.
program.option('-p, --port <number>', 'port number');
program.argument('[args...]', 'remote command and arguments'); // expecting zero or more arguments
program.action((args, options) => {
console.log(args);
});
Or you could suppress the error, useful for minimising changes in legacy code.
program.option('-p, --port', 'port number');
program.allowExcessArguments();
program.action((options) => {
console.log(program.args);
});
Stricter option flag parsing
Commander now throws an error for option flag combinations that are not supported. In particular, a short flag with multiple characters is now an error.
program.option('-ws, --workspace'); // throws error
A short option has a single character:
program.option('-w, --workspace');
Or from Commander 13.1 you can have an extra long flag instead of a short flag to allow a more memorable shortcut for the full name:
program.option('--ws, --workspace');
(Released in 13.0.0)
(Released in 13.0.0)
auto-detect special node flags node --eval and node --print when call .parse() with no arguments
node --eval and node --print when call .parse() with no arguments (#2164)node: (#2170).addHelpCommand() now takes a Command (passing string or boolean still works as before but deprecated)
.addHelpOption() as another way of configuring built-in help option (#2006).helpCommand() for configuring built-in help command (#2087)passThroughOptions constraints when using .addCommand and throw if parent command does not have .enablePositionalOptions() enabled (#1937).storeOptionsAsProperties() after setting an option value (#1928)@api private with documented @private (#1949).addHelpCommand() now takes a Command (passing string or boolean still works as before but deprecated) (#2087).addHelpCommand() passing string or boolean (use .helpCommand() or pass a Command) (#2087)program export instead) (#2017)global program
If you are using the deprecated default import of the global Command object, you need to switch to using a named import (or create a new Command).
// const program = require('commander');
const { program } = require('commander');
option and command clashes
A couple of configuration problems now throw an error, which will pick up issues in existing programs:
(Released in 12.0.0)
(Released in 12.0.0)
(Released in 12.0.0)
(Released in 12.0.0)
TypeScript: update OptionValueSource to allow any string, to match supported use of custom sources
OptionValueSource to allow any string, to match supported use of custom sources (#1983)Command.version() can also be used as getter (#1982)Commands.executableDir(), for when not configured (#1965)registeredArguments property on Command with the array of defined Argument (like Command.options for Option) (#2010)envVar, presetArg (#2019)argChoices, defaultValue, defaultValueDescription (#2019)Command._args was private anyway, but now available as registeredArguments (#2010)help command works when help option is disabled
improvements to documentation (#1858, #1859, #1860)
Option.optionFlags property from TypeScript definition (#1844).implies() (#1854)wrap command description in help
.getOptionValueSourceWithGlobals()
.getOptionValueSourceWithGlobals() (#1832)showGlobalOptions for .configureHelp{} and Help (#1828).setOptionValue() now also clears option source
.setOptionValue() now also clears option source (#1795)implied to OptionValueSource for option values set by using .implies() (#1794)undefined to return type of .getOptionValueSource() (#1794)preSubcommand hook called before direct subcommands
preSubcommand hook called before direct subcommands (#1763)InvalidOptionArgumentError in esm (#1756).summary() for a short summary to use instead of description when listing subcommands in help
.summary() for a short summary to use instead of description when listing subcommands in help (#1726)Option.implies() to set other option values when the option is specified (#1724)string[] to .options() default value parameter type for use with variadic options (#1721)-ws) (#1718)replace deprecated String.prototype.substr
String.prototype.substr (#1706)Option .conflicts() to set conflicting options which can not be specified together
.conflicts() to set conflicting options which can not be specified together (#1678)Option.preset() allows specifying value/arg for option when used without option-argument (especially optional, but also boolean option)
.executableDir() for custom search for subcommands (#1571)Option to .option() or .requiredOption() (#1655)error() for generating errors from client code just like Commander generated errors, with support for .configureOutput (), .exitOverride(), and .showHelpAfterError() (#1675).optsWithGlobals() to return merged local and global options (#1671)showSuggestionAfterError is now on by default (#1657)executableFile (#1571)executableFile (#1571).choices() (#1667).parse(), .parseAsync(), .aliases() (#1669)require.main.filename when script not known from arguments passed to .parse()
(can supply details using .name(), and .executableDir() or executableFile) (#1571)(Released in 9.0.0)
(Released in 9.0.0)
(Released in 9.0.0)
(Released in 9.0.0)
.getOptionValueSource() and .setOptionValueWithSource(), where expected values for source are one of 'default', 'env', 'config', 'cli'
.getOptionValueSource() and .setOptionValueWithSource(), where expected values for source are one of 'default', 'env', 'config', 'cli' (#1613).command('*'), use default command instead (#1612)on('command:*'), use .showSuggestionAfterError() instead (#1612).showSuggestionAfterError() to show suggestions after unknown command or unknown option
.showSuggestionAfterError() to show suggestions after unknown command or unknown option (#1590)Option support for values from environment variables using .env() (#1587)Option method argumentRejectedupdate Chinese translations for Commander v8
.copyInheritedSettings() (#1557)Argument methods for .argRequired() and .argOptional() (#1567).copyInheritedSettings() ([#1557])Argument methods for .argRequired() and .argOptional() ([#1567]).argument(name, description) for adding command-arguments
.argument(name, description) for adding command-arguments (#1490)
.createArgument() factory method (#1497).addArgument() (#1490)Argument supports .choices() (#1525).showHelpAfterError() to display full help or a custom message after an error (#1534).hook() with support for 'preAction' and 'postAction' callbacks (#1514).opts() return type using TypeScript generics (#1539).getOptionValue() and .setOptionValue() (#1521).parseAsync() is now declared as async (#1513)Help method .visibleArguments() returns array of Argument (#1490)CommanderError code commander.invalidOptionArgument renamed commander.invalidArgument (#1508).addTextHelp() callback no longer allows result of undefined, now just string (#1516)index.tab into a file per class (#1522).showHelpAfteError()) (#1534)Command property .arg initialised to empty array (was previously undefined) (#1529)cmd.description(desc, argDescriptions) for adding argument descriptions (#1490)
.argument(name, description) instead)InvalidOptionArgumentError (replaced by InvalidArgumentError) (#1508)Command object (#1520)
program export)If you have a simple program without an action handler, you will now get an error if there are missing command-arguments.
program
.option('-d, --debug')
.arguments('<file>');
program.parse();
$ node trivial.js
error: missing required argument 'file'
If you want to show the help in this situation, you could check the arguments before parsing:
if (process.argv.length === 2)
program.help();
program.parse();
Or, you might choose to show the help after any user error:
program.showHelpAfterError();
(Released in 8.0.0)
(Released in 8.0.0)
(Released in 8.0.0)
(Released in 8.0.0)
(Released in 8.0.0)
(Released in 8.0.0)
TypeScript typing for parent property on Command
parent property on Command (#1475).attributeName() on Option (#1483)replace use of deprecated process.mainModule
.cjs to list of expected script file extensions (#1449)process.mainModule (#1448)command('*') and call when command line includes options (#1464)on('command:*', ...) and call when command line includes unknown options (#1464)deprecated callback parameter to .help() and .outputHelp() (removed from README)
.enablePositionalOptions() to let program and subcommand reuse same option (#1427).passThroughOptions() to pass options through to other programs without needing -- (#1427).allowExcessArguments(false) to show an error message if there are too many command-arguments on command line for the action handler (#1409).configureOutput() to modify use of stdout and stderr or customise display of errors (#1387).addHelpText() to add text before or after the built-in help, for just current command or also for all subcommands (#1296).createOption() to support subclassing of automatically created options (like .createCommand()) (#1380)program.opts().storeOptionsAsProperties().help() and .outputHelp() (removed from README) (#1296)process.stderr.write() instead of console.error().on('--help') (removed from README) (#1296).passCommandToAction() (#1409)
.allowExcessArguments(false)The biggest change is the parsed option values. Previously the options were stored by default as properties on the command object, and now the options are stored separately.
If you wish to restore the old behaviour and get running quickly you can call .storeOptionsAsProperties().
To allow you to move to the new code patterns incrementally, the action handler will be passed the command twice,
to match the new "options" and "command" parameters (see below).
program options
Use the .opts() method to access the options. This is available on any command but is used most with the program.
program.option('-d, --debug');
program.parse();
// Old code before Commander 7
if (program.debug) console.log(`Program name is ${program.name()}`);
// New code
const options = program.opts();
if (options.debug) console.log(`Program name is ${program.name()}`);
action handler
The action handler gets passed a parameter for each command-argument you declared. Previously by default the next parameter was the command object with the options as properties. Now the next two parameters are instead the options and the command. If you only accessed the options there may be no code changes required.
program
.command('compress <filename>')
.option('-t, --trace')
// Old code before Commander 7
.action((filename, cmd) => {
if (cmd.trace) console.log(`Command name is ${cmd.name()}`);
});
// New code
.action((filename, options, command) => {
if (options.trace) console.log(`Command name is ${command.name()}`);
});
If you already set .storeOptionsAsProperties(false) you may still need to adjust your code.
program
.command('compress <filename>')
.storeOptionsAsProperties(false)
.option('-t, --trace')
// Old code before Commander 7
.action((filename, command) => {
if (command.opts().trace) console.log(`Command name is ${command.name()}`);
});
// New code
.action((filename, options, command) => {
if (command.opts().trace) console.log(`Command name is ${command.name()}`);
});
(Released in 7.0.0)
(Released in 7.0.0)
(Released in 7.0.0)
(Released in 7.0.0)
(Released in 7.0.0)
(Released in 7.0.0)
some tests failed if directory path included a space (1390)
added 'tsx' file extension for stand-alone executable subcommands
.description() to describe command arguments (#1353)include URL to relevant section of README for error for potential conflict between Command properties and option values
.combineFlagAndOptionalValue(false) to ease upgrade path from older versions of Commander (#1326).helpOption(false) (#1325)argumentDescription to .description() (#1323)add support for variadic options
-n accessed as opts().n (previously uppercase)(Released in 6.0.0)
(Released in 6.0.0)
support for multiple command aliases, the first of which is shown in the auto-generated help (#531, #1236)
addCommand() for hidden and isDefault (#1232)helpOption (#1248)arguments to improve auto-generated help in editors (#1235).command() configuration noHelp to hidden (but not remove old support) (#1232)support for nested commands with action-handlers (#1 #764 #1149)
.addCommand() for adding a separately configured command (#764 #1149).addHelpCommand() (#1149)-a -b -p 80 can be written as -abp80) (#1145).parseOption() includes short flag and long flag expansions (#1145).helpInformation() returns help text as a string, previously a private routine (#1169).parse() implicitly uses process.argv if arguments not specified (#1172).parse() arguments "from", if not following node conventions (#512 #1172)commands property of Command (#1184)program property (#1195)createCommand factory method to simplify subclassing (#1191)command:* for executable subcommands (#809 #1149).args contains command arguments with just recognised options removed (#1032 #1138).option() (#1119).allowUnknownOption() (#802 #1138)
.args-ab or --foo=bar) (#1145).parseOptions() (#1138)
args in returned result renamed operands and does not include anything after first unknown optionunknown in returned result has arguments after first unknown option including operands, not just options and values.on('command:*', callback) and other command events passed (changed) results from .parseOptions, i.e. operands and unknown (#1138)this rather than Command (#1180).parseAsync returns Promise<this> to be consistent with .parse() (#1180)@types/node (#1146)normalize (the functionality has been integrated into parseOptions) (#1145)parseExpectedArgs is now private (#1149)If you use .on('command:*') or more complicated tests to detect an unrecognised subcommand, you may be able to delete the code and rely on the default behaviour.
If you use program.args or more complicated tests to detect a missing subcommand, you may be able to delete the code and rely on the default behaviour.
If you use .command('*') to add a default command, you may be be able to switch to isDefault:true with a named command.
(Released in 5.0.0)
(Released in 5.0.0)
(Released in 5.0.0)
(Released in 5.0.0)
(Released in 5.0.0)
(Released in 5.0.0)
(Released in 5.0.0)
(Released in 5.0.0)
(Released in 5.0.0)
(Released in 5.0.0)
TypeScript definition for .action() should include Promise for async ([#1157])
.action() should include Promise for async ([#1157])two routines to change how option values are handled, and eliminate name clashes with command properties (#933 #1102)
.parseAsync to use instead of .parse if supply async action handlers (#806 #1118)ts-node in testsdisplay help when requested, even if there are missing required options
removed deprecated customFds option from call to child_process.spawn
.exitOverride() allows override of calls to process.exit for additional error handling and to keep program running (#1040).requiredOptions() (#1071)command:* event to include unknown argumentscustomFds option from call to child_process.spawn (#1052)If you were previously using code like:
if (!program.args.length) ...
a partial replacement is:
if (program.rawArgs.length < 3) ...
(Released in 4.0.0)
(Released in 4.0.0)
(Released in 4.0.0)
(Released in 4.0.0)
Improve tracking of executable subcommands.
Credits:
TypeScript definition for executableFile in CommandOptions
executableFile in CommandOptions (#1028)const rather than var in README (#1026)Breaking Change TypeScript to use overloaded function for .command. (#938 #990)
.command('clone', 'clone description', { executableFile: 'myClone' }).command to contrast action handler vs git-style executable. (#938 #990).command. (#938 #990)-p 80 can also be supplied as -p80node --harmony myCommand.js clone.version (#963)
program.version('0.0.1', '-v, --vers', 'output the current version').helpOption(flags, description) routine to customise help flags and description (#963)
.helpOption('-e, --HELP', 'read more information')--foo and --no-foo--no-foo on cli now emits option:no-foo (previously option:foo)--no-foo after defining --foo leaves the default value unchanged (previously set it to false)node --inspect myCommand.js cloneThe custom event for a negated option like --no-foo is option:no-foo (previously option:foo).
program
.option('--no-foo')
.on('option:no-foo', () => {
console.log('removing foo');
});
When using TypeScript, adding a command does not allow an explicit undefined for an unwanted executable description (e.g. for a command with an action handler).
program
.command('action1', undefined, { noHelp: true }) // No longer valid
.command('action2', { noHelp: true }) // Correct
Your coding agent can read these notes before it upgrades. Set up the MCP server →