Commando uses Commander.js for rich CLI features while keeping command definitions simple.
Commando commands (simple) → Framework bridge → Commander (rich CLI)
┌──────────────────┐
│ CommandoCommand │ Simple command definition
│ - description │
│ - execute() │
└────────┬─────────┘
│
▼
┌──────────────────┐
│ Framework │ Translates to Commander
│ buildCommander() │
└────────┬─────────┘
│
▼
┌──────────────────┐
│ Commander.js │ Handles parsing, help, validation
│ - parse() │
│ - help() │
└──────────────────┘
export const deploy: CommandoCommand = {
description: 'Deploy website',
execute: async (options, args, context) => {
console.log('Deploying...');
}
};// lib/core.ts
function buildCommanderCommand(
name: string,
cmdoCmd: CommandoCommand,
context: CommandoContext
): Command {
const cmd = new Command(name);
cmd.description(cmdoCmd.description);
// Let command customize if needed
if (cmdoCmd.defineCommand) {
cmdoCmd.defineCommand(cmd);
}
// Install action handler
cmd.action(async (...args) => {
const options = args[args.length - 2];
const positionalArgs = args.slice(0, -2);
await cmdoCmd.execute(options, positionalArgs, context);
});
return cmd;
}User runs: cmdo deploy staging --skip-tests
Commander:
- Matches
deploycommand - Parses
stagingas argument - Parses
--skip-testsas flag - Invokes action handler
execute: async (options, args, context) => {
// options.skipTests = true
// args[0] = 'staging'
// Already parsed!
}Use defineCommand() for Commander-specific options:
export const deploy: CommandoCommand = {
description: 'Deploy website',
defineCommand: (cmd) => {
cmd
.argument('<environment>', 'Target environment')
.option('-s, --skip-tests', 'Skip tests')
.option('--dry-run', 'Preview only');
},
execute: async (options, args) => {
const env = args[0]; // 'staging'
if (options.skipTests) { ... } // true/false
if (options.dryRun) { ... } // true/false
}
};cmdo deploy --helpOutput:
Usage: cmdo deploy [options] <environment>
Deploy website
Arguments:
environment Target environment
Options:
-s, --skip-tests Skip tests
--dry-run Preview only
-h, --help display help
cmdo deploy # Missing required argument
# Error: missing required argument 'environment'import { Command } from 'commander';
defineCommand: (cmd: Command) => {
cmd.option('--count <n>', 'Count', parseInt);
// ^^^^^^^ Type coercion
}Framework adds global flags automatically:
cmdo --version # Show version
cmdo --root /path/to/proj # Override project root
cmdo -d # Debug mode (verbose logging)
cmdo -q # Quiet mode
cmdo -s # Silent mode
cmdo --no-color # Disable colors
cmdo --log-format json # JSON logsThese are configured in lib/cli.ts before commands load.
Groups are Commander subcommands:
// Framework creates:
const program = new Command('cmdo');
const websiteCmd = new Command('website');
websiteCmd.addCommand(buildCommand('build', ...));
websiteCmd.addCommand(buildCommand('deploy', ...));
program.addCommand(websiteCmd);Result: cmdo website build, cmdo website deploy
You need it if:
- Custom arguments (required, optional, variadic)
- Options with choices/validation
- Default values
- Type coercion (parse numbers, dates)
You don't need it if:
- Simple command with no options
- Just needs description and execute
export const version = {
description: 'Show version',
execute: async () => console.log('v2.0.0')
};export const build: CommandoCommand = {
description: 'Build website',
defineCommand: (cmd) => {
cmd
.option('-c, --clean', 'Clean first')
.option('--no-optimize', 'Skip optimization');
},
execute: async (options) => {
if (options.clean) await clean();
if (options.optimize) await optimize(); // --no-optimize negates
await build();
}
};export const deploy: CommandoCommand = {
description: 'Deploy to environment',
defineCommand: (cmd) => {
cmd.argument('<env>', 'Environment name');
},
execute: async (options, args) => {
const env = args[0]; // Required
await deploy(env);
}
};✅ Simple commands stay simple - Just description + execute ✅ Rich features available - Full Commander power when needed ✅ Auto-help - Generated from definitions ✅ Type-safe - TypeScript validates everything ✅ Separation - Commando commands don't know about Commander
- Auto-Discovery - How commands are discovered
- Commander.js docs - Full Commander API
- docs/libraries/commander.md - Commander reference