super-commander
A fluent, deferred-registration wrapper around Commander.
Register subcommands and global options in any order; the library assembles
everything at .parse() so each module can independently contribute its own
commands and options.
Installation
npm install super-commander
# or
pnpm add super-commanderRequires Node.js >= 22.12.0.
Quick start
import { SuperCommander } from 'super-commander';
const cli = new SuperCommander('myapp', (cmd) =>
cmd.description('My awesome CLI').version('1.0.0')
);
// Register in any order; everything is deferred until parse()
cli.addOption('--verbose', 'Enable verbose output');
cli.addSubCommand('upload', (cmd) =>
cmd
.description('Upload a file')
.argument('<file>')
.action((file) => console.log('Uploading:', file))
);
cli.addSubCommand('download', (cmd) =>
cmd
.description('Download a file')
.argument('<url>')
.action((url) => console.log('Downloading:', url))
);
cli.parse();Why super-commander?
super-commander gives you a fluent, order-independent API for building
Commander CLIs. Register options and subcommands as you go; the library defers
everything until .parse(), so you're free to compose your CLI from
independent modules without worrying about what got registered first.
import { SuperCommander } from 'super-commander';
const cli = new SuperCommander('myapp');
// Modules can register their own commands and options independently
function addVerboseOption(cli: SuperCommander) {
cli.addOption('--verbose', 'Enable verbose output');
}
function addUploadCommand(cli: SuperCommander) {
cli.addSubCommand('upload', (cmd) =>
cmd.description('Upload a file').argument('<file>').action(handler)
);
}
addVerboseOption(cli);
addUploadCommand(cli);
cli.parse(); // Everything assembled here; order doesn't matterThe nested command trees (addSuperSubCommand) let you build deep CLI
structures with the same fluent style, each subtree managing its own options
and subcommands.
API
new SuperCommander(programName, fn?)
Create a new CLI program. programName sets the root command name shown in
help output. The optional fn receives the root Command for setting
.description(), .version(), etc.
.addSubCommand(name, fn?)
Register a subcommand. Pass a config callback as fn to set up the command
(.description(), .argument(), .action(), etc.), or omit it for a bare
subcommand. Throws if name is already registered.
// With config callback
cli.addSubCommand('upload', (cmd) =>
cmd.description('Upload a file').argument('<file>').action(handler)
);
// Bare subcommand
cli.addSubCommand('status');.addSuperSubCommand(subCmd)
Register a pre-built SuperSubCommand instance. Throws if a subcommand with
the same name is already registered.
.addSuperSubCommand(name, fn)
Register a nested command tree. fn receives a fresh SuperSubCommand
(which has all the same methods as SuperCommander). Use it when a subcommand
needs its own subcommands, global options, and help configuration. Throws if
name is already registered.
cli.addSuperSubCommand('admin', (admin) => {
admin.withGlobal((cmd) => cmd.description('Admin commands'));
admin.addSubCommand('users', (cmd) =>
cmd.description('Manage users').action(() => {})
);
admin.addSubCommand('roles', (cmd) =>
cmd.description('Manage roles').action(() => {})
);
});.addOption(flags, description?, fn?)
Register a global option added to the root command at parse time. Throws if the option name is already registered.
// Flags + description
cli.addOption('--verbose', 'Enable verbose output');
// Flags + description + config callback (e.g. set a default)
cli.addOption('--port <number>', 'Port to listen on', (opt) =>
opt.default(3000).env('PORT')
);
// Flags + config callback (skip description)
cli.addOption('--debug', (opt) => opt.env('DEBUG').default(false));.withGlobal(fn)
Escape hatch: replace the root Command via a callback. Use for Commander
APIs the wrapper doesn't expose (.hook(), .showSuggestionAfterError(), etc.).
cli.withGlobal((cmd) => cmd.showSuggestionAfterError(true));.configureHelp(config)
Set HelpConfiguration
applied to the root command and all subcommands at parse time.
.parse(argv?, parseOptions?)
Assemble the full command tree and parse argv synchronously (defaults to
process.argv). Returns the parsed root Command.
.parseAsync(argv?, parseOptions?)
Like .parse() but returns a Promise<Command>. Use when a command action
is async and you need to await the result.
.command
Access the underlying root Command. Useful for inspecting the assembled tree
after parsing.
.getName()
Returns the program name.
Re-exports
The package re-exports Commander types from the main entry for convenience:
import { SuperCommander, SuperSubCommand, Command, Option } from 'super-commander';
import type { HelpConfiguration, ParseOptions } from 'super-commander';A dedicated super-commander/commander entry point re-exports everything from
Commander:
import { Command, Option, Argument, program } from 'super-commander/commander';
import type { HelpConfiguration, ParseOptions } from 'super-commander/commander';
## License
[ISC](LICENSE)