Files
gh-vanman2024-cli-builder-p…/skills/commander-patterns/SKILL.md
2025-11-30 09:04:14 +08:00

436 lines
12 KiB
Markdown

---
name: Commander.js Patterns
description: Commander.js CLI framework patterns including Command class, options, arguments, nested subcommands, and Option class usage. Use when building Node.js CLIs, implementing Commander.js commands, creating TypeScript CLI tools, adding command options/arguments, or when user mentions Commander.js, CLI commands, command options, or nested subcommands.
allowed-tools: Read, Write, Bash, Edit
---
# Commander.js Patterns Skill
Provides comprehensive Commander.js patterns, templates, and examples for building robust Node.js CLI applications with TypeScript support.
## Overview
Commander.js is the complete solution for Node.js command-line interfaces. This skill provides battle-tested patterns for:
- Command class instantiation and configuration
- Options with flags, choices, and defaults
- Arguments (required, optional, variadic)
- Nested subcommands and command hierarchies
- Option class with advanced validation
- Action handlers and middleware
- Error handling and validation
## Instructions
### Basic Command Setup
1. **Create program instance:**
```typescript
import { Command } from 'commander';
const program = new Command();
program
.name('mycli')
.description('CLI description')
.version('1.0.0');
```
2. **Add simple command:**
```typescript
program
.command('init')
.description('Initialize project')
.action(() => {
// Command logic
});
```
3. **Parse arguments:**
```typescript
program.parse();
```
### Command with Options
Use options for named flags with values:
```typescript
program
.command('deploy')
.description('Deploy application')
.option('-e, --env <environment>', 'target environment', 'dev')
.option('-f, --force', 'force deployment', false)
.option('-v, --verbose', 'verbose output')
.action((options) => {
console.log('Environment:', options.env);
console.log('Force:', options.force);
console.log('Verbose:', options.verbose);
});
```
### Command with Arguments
Use arguments for positional parameters:
```typescript
program
.command('deploy <environment>')
.description('Deploy to environment')
.argument('<environment>', 'target environment')
.argument('[region]', 'optional region', 'us-east-1')
.action((environment, region, options) => {
console.log(`Deploying to ${environment} in ${region}`);
});
```
### Option Class Usage
For advanced option configuration:
```typescript
import { Command, Option } from 'commander';
program
.command('deploy')
.addOption(
new Option('-m, --mode <mode>', 'deployment mode')
.choices(['fast', 'safe', 'rollback'])
.default('safe')
.makeOptionMandatory()
)
.addOption(
new Option('-r, --replicas <count>', 'replica count')
.argParser(parseInt)
.default(3)
)
.action((options) => {
console.log(`Mode: ${options.mode}, Replicas: ${options.replicas}`);
});
```
### Nested Subcommands
Create command hierarchies:
```typescript
const config = program
.command('config')
.description('Manage configuration');
config
.command('get <key>')
.description('Get config value')
.action((key) => {
console.log(`Config ${key}:`, getConfig(key));
});
config
.command('set <key> <value>')
.description('Set config value')
.action((key, value) => {
setConfig(key, value);
console.log(`✓ Set ${key} = ${value}`);
});
config
.command('list')
.description('List all config')
.action(() => {
console.log(getAllConfig());
});
```
### Variadic Arguments
Accept multiple values:
```typescript
program
.command('add <items...>')
.description('Add multiple items')
.action((items) => {
console.log('Adding items:', items);
});
// Usage: mycli add item1 item2 item3
```
### Custom Argument Parsing
Transform argument values:
```typescript
program
.command('wait <delay>')
.description('Wait for specified time')
.argument('<delay>', 'delay in seconds', parseFloat)
.action((delay) => {
console.log(`Waiting ${delay} seconds...`);
});
```
### Global Options
Options available to all commands:
```typescript
program
.option('-c, --config <path>', 'config file path')
.option('-v, --verbose', 'verbose output')
.option('--no-color', 'disable colors');
program
.command('deploy')
.action((options, command) => {
const globalOpts = command.parent?.opts();
console.log('Config:', globalOpts?.config);
console.log('Verbose:', globalOpts?.verbose);
});
```
### Error Handling
```typescript
program
.command('deploy <environment>')
.action((environment) => {
if (!['dev', 'staging', 'prod'].includes(environment)) {
throw new Error(`Invalid environment: ${environment}`);
}
// Deploy logic
});
program.exitOverride();
try {
program.parse();
} catch (err) {
console.error('Error:', err.message);
process.exit(1);
}
```
## Available Scripts
- **validate-commander-structure.sh**: Validates Commander.js CLI structure and patterns
- **generate-command.sh**: Scaffolds new command with options and arguments
- **generate-subcommand.sh**: Creates nested subcommand structure
- **test-commander-cli.sh**: Tests CLI commands with various inputs
- **extract-command-help.sh**: Extracts help text from CLI for documentation
## Templates
### TypeScript Templates
- **basic-commander.ts**: Minimal Commander.js setup
- **command-with-options.ts**: Command with various option types
- **command-with-arguments.ts**: Command with required/optional arguments
- **nested-subcommands.ts**: Multi-level command hierarchy
- **option-class-advanced.ts**: Advanced Option class usage
- **full-cli-example.ts**: Complete CLI with all patterns
- **commander-with-inquirer.ts**: Interactive prompts integration
- **commander-with-validation.ts**: Input validation patterns
### JavaScript Templates
- **basic-commander.js**: ES modules Commander.js setup
- **commonjs-commander.js**: CommonJS Commander.js setup
### Configuration Templates
- **tsconfig.commander.json**: TypeScript config for Commander.js projects
- **package.json.template**: Package.json with Commander.js dependencies
## Examples
- **basic-usage.md**: Simple CLI with 2-3 commands
- **options-arguments-demo.md**: Comprehensive options and arguments examples
- **nested-commands-demo.md**: Building command hierarchies
- **advanced-option-class.md**: Option class validation and parsing
- **interactive-cli.md**: Combining Commander.js with Inquirer.js
- **error-handling-patterns.md**: Robust error handling strategies
- **testing-commander-cli.md**: Unit and integration testing patterns
## Commander.js Key Concepts
### Command Class
```typescript
new Command()
.name('cli-name')
.description('CLI description')
.version('1.0.0')
.command('subcommand')
```
### Option Types
- **Flag option**: `-v, --verbose` (boolean)
- **Value option**: `-p, --port <port>` (required value)
- **Optional value**: `-p, --port [port]` (optional value)
- **Negatable**: `--no-color` (inverse boolean)
- **Variadic**: `--files <files...>` (multiple values)
### Argument Types
- **Required**: `<name>`
- **Optional**: `[name]`
- **Variadic**: `<items...>` or `[items...]`
### Option Class Methods
- `.choices(['a', 'b', 'c'])`: Restrict to specific values
- `.default(value)`: Set default value
- `.argParser(fn)`: Custom parsing function
- `.makeOptionMandatory()`: Require option
- `.conflicts(option)`: Mutually exclusive options
- `.implies(option)`: Implies another option
- `.env(name)`: Read from environment variable
### Action Handler Signatures
```typescript
// No arguments
.action(() => {})
// With options only
.action((options) => {})
// With arguments
.action((arg1, arg2, options) => {})
// With command reference
.action((options, command) => {})
```
## Pattern Recipes
### Pattern 1: Simple CLI with Subcommands
Use template: `templates/basic-commander.ts`
### Pattern 2: CLI with Rich Options
Use template: `templates/option-class-advanced.ts`
### Pattern 3: Interactive CLI
Use template: `templates/commander-with-inquirer.ts`
### Pattern 4: CLI with Validation
Use template: `templates/commander-with-validation.ts`
### Pattern 5: Multi-level Commands
Use template: `templates/nested-subcommands.ts`
## Integration with Other Tools
### With Inquirer.js (Interactive Prompts)
```typescript
import inquirer from 'inquirer';
program
.command('setup')
.action(async () => {
const answers = await inquirer.prompt([
{ type: 'input', name: 'name', message: 'Project name:' },
{ type: 'list', name: 'template', message: 'Template:', choices: ['basic', 'advanced'] }
]);
// Use answers
});
```
### With Chalk (Colored Output)
```typescript
import chalk from 'chalk';
program
.command('deploy')
.action(() => {
console.log(chalk.green('✓ Deployment successful'));
console.log(chalk.red('✗ Deployment failed'));
});
```
### With Ora (Spinners)
```typescript
import ora from 'ora';
program
.command('build')
.action(async () => {
const spinner = ora('Building...').start();
await build();
spinner.succeed('Build complete');
});
```
## Best Practices
1. **Use Option class for complex options**: Provides better validation and type safety
2. **Keep action handlers thin**: Delegate to separate functions
3. **Provide clear descriptions**: Help users understand commands
4. **Set sensible defaults**: Reduce required options
5. **Validate early**: Check inputs before processing
6. **Handle errors gracefully**: Provide helpful error messages
7. **Use TypeScript**: Better type safety and IDE support
8. **Test thoroughly**: Unit test commands and options
9. **Document examples**: Show common usage patterns
10. **Version your CLI**: Use semantic versioning
## Common Patterns
### Pattern: Config Command Group
```typescript
const config = program.command('config');
config.command('get <key>').action(getConfig);
config.command('set <key> <value>').action(setConfig);
config.command('list').action(listConfig);
config.command('delete <key>').action(deleteConfig);
```
### Pattern: CRUD Commands
```typescript
program.command('create <name>').action(create);
program.command('read <id>').action(read);
program.command('update <id>').action(update);
program.command('delete <id>').action(deleteItem);
program.command('list').action(list);
```
### Pattern: Deploy with Environments
```typescript
program
.command('deploy')
.addOption(new Option('-e, --env <env>').choices(['dev', 'staging', 'prod']))
.option('-f, --force', 'force deployment')
.action(deploy);
```
## Troubleshooting
### Issue: Options not parsed
**Solution**: Ensure `program.parse()` is called
### Issue: Arguments not received
**Solution**: Check action handler signature matches argument count
### Issue: Subcommands not working
**Solution**: Verify subcommand is attached before `parse()`
### Issue: TypeScript errors
**Solution**: Install `@types/node` and configure tsconfig
### Issue: Help not showing
**Solution**: Commander.js auto-generates help from descriptions
## Success Criteria
✅ Command structure follows Commander.js conventions
✅ Options and arguments properly typed
✅ Help text is clear and descriptive
✅ Error handling covers edge cases
✅ CLI tested with various inputs
✅ TypeScript compiles without errors
✅ Commands execute as expected
## Related Skills
- `click-patterns` - Python Click framework patterns
- `typer-patterns` - Python Typer framework patterns
- `clap-patterns` - Rust Clap framework patterns
---
**Skill Type**: Framework Patterns + Code Templates
**Language**: TypeScript/JavaScript (Node.js)
**Framework**: Commander.js v12+
**Auto-invocation**: Yes (via description matching)