Initial commit
This commit is contained in:
435
skills/commander-patterns/SKILL.md
Normal file
435
skills/commander-patterns/SKILL.md
Normal file
@@ -0,0 +1,435 @@
|
||||
---
|
||||
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)
|
||||
513
skills/commander-patterns/examples/advanced-option-class.md
Normal file
513
skills/commander-patterns/examples/advanced-option-class.md
Normal file
@@ -0,0 +1,513 @@
|
||||
# Advanced Option Class Usage
|
||||
|
||||
Comprehensive examples of the Option class for advanced option handling.
|
||||
|
||||
## Basic Option Class
|
||||
|
||||
```typescript
|
||||
import { Command, Option } from 'commander';
|
||||
|
||||
const program = new Command();
|
||||
|
||||
program
|
||||
.command('deploy')
|
||||
.addOption(
|
||||
new Option('-e, --env <environment>', 'target environment')
|
||||
.choices(['dev', 'staging', 'prod'])
|
||||
.default('dev')
|
||||
)
|
||||
.action((options) => {
|
||||
console.log('Environment:', options.env);
|
||||
});
|
||||
|
||||
program.parse();
|
||||
```
|
||||
|
||||
## Option with Choices
|
||||
|
||||
```typescript
|
||||
import { Command, Option } from 'commander';
|
||||
|
||||
program
|
||||
.command('log')
|
||||
.addOption(
|
||||
new Option('-l, --level <level>', 'log level')
|
||||
.choices(['debug', 'info', 'warn', 'error'])
|
||||
.default('info')
|
||||
)
|
||||
.addOption(
|
||||
new Option('-f, --format <format>', 'output format')
|
||||
.choices(['json', 'yaml', 'table'])
|
||||
.default('table')
|
||||
)
|
||||
.action((options) => {
|
||||
console.log(`Logging at ${options.level} level in ${options.format} format`);
|
||||
});
|
||||
```
|
||||
|
||||
Usage:
|
||||
```bash
|
||||
mycli log --level debug --format json
|
||||
mycli log --level invalid # Error: invalid choice
|
||||
```
|
||||
|
||||
## Mandatory Options
|
||||
|
||||
```typescript
|
||||
import { Command, Option } from 'commander';
|
||||
|
||||
program
|
||||
.command('deploy')
|
||||
.addOption(
|
||||
new Option('-t, --token <token>', 'API token')
|
||||
.makeOptionMandatory()
|
||||
)
|
||||
.addOption(
|
||||
new Option('-e, --env <environment>', 'environment')
|
||||
.choices(['dev', 'staging', 'prod'])
|
||||
.makeOptionMandatory()
|
||||
)
|
||||
.action((options) => {
|
||||
console.log('Deploying with token:', options.token);
|
||||
console.log('Environment:', options.env);
|
||||
});
|
||||
```
|
||||
|
||||
Usage:
|
||||
```bash
|
||||
mycli deploy # Error: required option missing
|
||||
mycli deploy --token abc --env prod # ✓ Works
|
||||
```
|
||||
|
||||
## Options from Environment Variables
|
||||
|
||||
```typescript
|
||||
import { Command, Option } from 'commander';
|
||||
|
||||
program
|
||||
.command('deploy')
|
||||
.addOption(
|
||||
new Option('-t, --token <token>', 'API token')
|
||||
.env('API_TOKEN')
|
||||
.makeOptionMandatory()
|
||||
)
|
||||
.addOption(
|
||||
new Option('-u, --api-url <url>', 'API URL')
|
||||
.env('API_URL')
|
||||
.default('https://api.example.com')
|
||||
)
|
||||
.action((options) => {
|
||||
console.log('Token:', options.token);
|
||||
console.log('API URL:', options.apiUrl);
|
||||
});
|
||||
```
|
||||
|
||||
Usage:
|
||||
```bash
|
||||
export API_TOKEN=abc123
|
||||
export API_URL=https://custom-api.com
|
||||
|
||||
mycli deploy # Uses environment variables
|
||||
mycli deploy --token xyz # CLI arg overrides env var
|
||||
```
|
||||
|
||||
## Custom Argument Parsers
|
||||
|
||||
```typescript
|
||||
import { Command, Option } from 'commander';
|
||||
|
||||
program
|
||||
.command('scale')
|
||||
.addOption(
|
||||
new Option('-r, --replicas <count>', 'number of replicas')
|
||||
.argParser((value) => {
|
||||
const count = parseInt(value, 10);
|
||||
if (isNaN(count) || count < 1 || count > 100) {
|
||||
throw new Error('Replicas must be between 1 and 100');
|
||||
}
|
||||
return count;
|
||||
})
|
||||
.default(3)
|
||||
)
|
||||
.addOption(
|
||||
new Option('-m, --memory <size>', 'memory limit')
|
||||
.argParser((value) => {
|
||||
// Parse sizes like "512M", "2G"
|
||||
const match = value.match(/^(\d+)([MG])$/i);
|
||||
if (!match) {
|
||||
throw new Error('Invalid memory format (use 512M or 2G)');
|
||||
}
|
||||
const [, num, unit] = match;
|
||||
const mb = parseInt(num) * (unit.toUpperCase() === 'G' ? 1024 : 1);
|
||||
return mb;
|
||||
})
|
||||
.default(512)
|
||||
)
|
||||
.action((options) => {
|
||||
console.log('Replicas:', options.replicas);
|
||||
console.log('Memory:', options.memory, 'MB');
|
||||
});
|
||||
```
|
||||
|
||||
Usage:
|
||||
```bash
|
||||
mycli scale --replicas 5 --memory 2G
|
||||
# Replicas: 5
|
||||
# Memory: 2048 MB
|
||||
```
|
||||
|
||||
## Conflicting Options
|
||||
|
||||
```typescript
|
||||
import { Command, Option } from 'commander';
|
||||
|
||||
program
|
||||
.command('build')
|
||||
.addOption(
|
||||
new Option('--cache', 'enable caching')
|
||||
.conflicts('noCache')
|
||||
)
|
||||
.addOption(
|
||||
new Option('--no-cache', 'disable caching')
|
||||
.conflicts('cache')
|
||||
)
|
||||
.addOption(
|
||||
new Option('--watch', 'watch mode')
|
||||
.conflicts('production')
|
||||
)
|
||||
.addOption(
|
||||
new Option('--production', 'production build')
|
||||
.conflicts('watch')
|
||||
)
|
||||
.action((options) => {
|
||||
console.log('Cache:', options.cache);
|
||||
console.log('Watch:', options.watch);
|
||||
console.log('Production:', options.production);
|
||||
});
|
||||
```
|
||||
|
||||
Usage:
|
||||
```bash
|
||||
mycli build --cache # ✓ Works
|
||||
mycli build --no-cache # ✓ Works
|
||||
mycli build --cache --no-cache # ✗ Error: conflicting options
|
||||
mycli build --watch --production # ✗ Error: conflicting options
|
||||
```
|
||||
|
||||
## Option Implies
|
||||
|
||||
```typescript
|
||||
import { Command, Option } from 'commander';
|
||||
|
||||
program
|
||||
.command('server')
|
||||
.addOption(
|
||||
new Option('--ssl', 'enable SSL')
|
||||
.implies({ sslCert: './cert.pem', sslKey: './key.pem' })
|
||||
)
|
||||
.addOption(
|
||||
new Option('--ssl-cert <path>', 'SSL certificate path')
|
||||
)
|
||||
.addOption(
|
||||
new Option('--ssl-key <path>', 'SSL key path')
|
||||
)
|
||||
.addOption(
|
||||
new Option('--secure', 'secure mode')
|
||||
.implies({ ssl: true, httpsOnly: true })
|
||||
)
|
||||
.addOption(
|
||||
new Option('--https-only', 'enforce HTTPS')
|
||||
)
|
||||
.action((options) => {
|
||||
console.log('SSL:', options.ssl);
|
||||
console.log('SSL Cert:', options.sslCert);
|
||||
console.log('SSL Key:', options.sslKey);
|
||||
console.log('HTTPS Only:', options.httpsOnly);
|
||||
});
|
||||
```
|
||||
|
||||
Usage:
|
||||
```bash
|
||||
mycli server --ssl
|
||||
# SSL: true
|
||||
# SSL Cert: ./cert.pem
|
||||
# SSL Key: ./key.pem
|
||||
|
||||
mycli server --secure
|
||||
# SSL: true
|
||||
# HTTPS Only: true
|
||||
```
|
||||
|
||||
## Preset Configurations
|
||||
|
||||
```typescript
|
||||
import { Command, Option } from 'commander';
|
||||
|
||||
program
|
||||
.command('deploy')
|
||||
.addOption(
|
||||
new Option('--preset <preset>', 'use preset configuration')
|
||||
.choices(['minimal', 'standard', 'enterprise'])
|
||||
.argParser((value) => {
|
||||
const presets = {
|
||||
minimal: {
|
||||
replicas: 1,
|
||||
memory: '256M',
|
||||
cpu: '0.25',
|
||||
autoScaling: false,
|
||||
},
|
||||
standard: {
|
||||
replicas: 3,
|
||||
memory: '512M',
|
||||
cpu: '0.5',
|
||||
autoScaling: true,
|
||||
},
|
||||
enterprise: {
|
||||
replicas: 10,
|
||||
memory: '2G',
|
||||
cpu: '2',
|
||||
autoScaling: true,
|
||||
loadBalancer: true,
|
||||
monitoring: true,
|
||||
},
|
||||
};
|
||||
return presets[value as keyof typeof presets];
|
||||
})
|
||||
)
|
||||
.action((options) => {
|
||||
if (options.preset) {
|
||||
console.log('Using preset configuration:');
|
||||
console.log(JSON.stringify(options.preset, null, 2));
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
Usage:
|
||||
```bash
|
||||
mycli deploy --preset enterprise
|
||||
# Using preset configuration:
|
||||
# {
|
||||
# "replicas": 10,
|
||||
# "memory": "2G",
|
||||
# "cpu": "2",
|
||||
# "autoScaling": true,
|
||||
# "loadBalancer": true,
|
||||
# "monitoring": true
|
||||
# }
|
||||
```
|
||||
|
||||
## Hidden Options (Debug/Internal)
|
||||
|
||||
```typescript
|
||||
import { Command, Option } from 'commander';
|
||||
|
||||
program
|
||||
.command('build')
|
||||
.option('-p, --production', 'production build')
|
||||
.addOption(
|
||||
new Option('--debug', 'enable debug mode')
|
||||
.hideHelp()
|
||||
)
|
||||
.addOption(
|
||||
new Option('--internal-api', 'use internal API')
|
||||
.hideHelp()
|
||||
.default(false)
|
||||
)
|
||||
.action((options) => {
|
||||
if (options.debug) {
|
||||
console.log('Debug mode enabled');
|
||||
console.log('All options:', options);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Complex Validation
|
||||
|
||||
```typescript
|
||||
import { Command, Option } from 'commander';
|
||||
|
||||
program
|
||||
.command('backup')
|
||||
.addOption(
|
||||
new Option('-s, --schedule <cron>', 'backup schedule (cron format)')
|
||||
.argParser((value) => {
|
||||
// Validate cron expression
|
||||
const cronRegex = /^(\*|([0-9]|1[0-9]|2[0-9]|3[0-9]|4[0-9]|5[0-9])|\*\/([0-9]|1[0-9]|2[0-9]|3[0-9]|4[0-9]|5[0-9])) (\*|([0-9]|1[0-9]|2[0-3])|\*\/([0-9]|1[0-9]|2[0-3])) (\*|([1-9]|1[0-9]|2[0-9]|3[0-1])|\*\/([1-9]|1[0-9]|2[0-9]|3[0-1])) (\*|([1-9]|1[0-2])|\*\/([1-9]|1[0-2])) (\*|([0-6])|\*\/([0-6]))$/;
|
||||
|
||||
if (!cronRegex.test(value)) {
|
||||
throw new Error('Invalid cron expression');
|
||||
}
|
||||
return value;
|
||||
})
|
||||
)
|
||||
.addOption(
|
||||
new Option('-r, --retention <days>', 'backup retention in days')
|
||||
.argParser((value) => {
|
||||
const days = parseInt(value, 10);
|
||||
if (isNaN(days) || days < 1 || days > 365) {
|
||||
throw new Error('Retention must be between 1 and 365 days');
|
||||
}
|
||||
return days;
|
||||
})
|
||||
.default(30)
|
||||
)
|
||||
.action((options) => {
|
||||
console.log('Schedule:', options.schedule);
|
||||
console.log('Retention:', options.retention, 'days');
|
||||
});
|
||||
```
|
||||
|
||||
Usage:
|
||||
```bash
|
||||
mycli backup --schedule "0 2 * * *" --retention 90
|
||||
# Schedule: 0 2 * * *
|
||||
# Retention: 90 days
|
||||
```
|
||||
|
||||
## Cumulative Options
|
||||
|
||||
```typescript
|
||||
import { Command, Option } from 'commander';
|
||||
|
||||
program
|
||||
.command('log')
|
||||
.addOption(
|
||||
new Option('-v, --verbose', 'increase verbosity')
|
||||
.argParser((value, previous) => {
|
||||
return previous + 1;
|
||||
})
|
||||
.default(0)
|
||||
)
|
||||
.action((options) => {
|
||||
console.log('Verbosity level:', options.verbose);
|
||||
// 0 = normal
|
||||
// 1 = verbose (-v)
|
||||
// 2 = very verbose (-vv)
|
||||
// 3 = debug (-vvv)
|
||||
});
|
||||
```
|
||||
|
||||
Usage:
|
||||
```bash
|
||||
mycli log # verbosity: 0
|
||||
mycli log -v # verbosity: 1
|
||||
mycli log -vv # verbosity: 2
|
||||
mycli log -vvv # verbosity: 3
|
||||
```
|
||||
|
||||
## Complete Example
|
||||
|
||||
```typescript
|
||||
#!/usr/bin/env node
|
||||
import { Command, Option } from 'commander';
|
||||
import chalk from 'chalk';
|
||||
|
||||
const program = new Command();
|
||||
|
||||
program
|
||||
.name('deploy-cli')
|
||||
.version('1.0.0');
|
||||
|
||||
program
|
||||
.command('deploy')
|
||||
.description('Deploy application with advanced options')
|
||||
|
||||
// Required with choices
|
||||
.addOption(
|
||||
new Option('-e, --env <environment>', 'deployment environment')
|
||||
.choices(['dev', 'staging', 'prod'])
|
||||
.makeOptionMandatory()
|
||||
)
|
||||
|
||||
// Environment variable with validation
|
||||
.addOption(
|
||||
new Option('-t, --token <token>', 'API token')
|
||||
.env('DEPLOY_TOKEN')
|
||||
.makeOptionMandatory()
|
||||
.argParser((value) => {
|
||||
if (value.length < 32) {
|
||||
throw new Error('Token must be at least 32 characters');
|
||||
}
|
||||
return value;
|
||||
})
|
||||
)
|
||||
|
||||
// Custom parser with unit conversion
|
||||
.addOption(
|
||||
new Option('-m, --memory <size>', 'memory allocation')
|
||||
.argParser((value) => {
|
||||
const match = value.match(/^(\d+)([MG])$/i);
|
||||
if (!match) {
|
||||
throw new Error('Memory must be in format: 512M or 2G');
|
||||
}
|
||||
const [, num, unit] = match;
|
||||
return parseInt(num) * (unit.toUpperCase() === 'G' ? 1024 : 1);
|
||||
})
|
||||
.default(512)
|
||||
)
|
||||
|
||||
// Conflicting options
|
||||
.addOption(
|
||||
new Option('--fast', 'fast deployment (skip tests)')
|
||||
.conflicts('safe')
|
||||
)
|
||||
.addOption(
|
||||
new Option('--safe', 'safe deployment (full tests)')
|
||||
.conflicts('fast')
|
||||
.default(true)
|
||||
)
|
||||
|
||||
// Implies relationship
|
||||
.addOption(
|
||||
new Option('--production-mode', 'enable production optimizations')
|
||||
.implies({ env: 'prod', safe: true, monitoring: true })
|
||||
)
|
||||
.addOption(
|
||||
new Option('--monitoring', 'enable monitoring')
|
||||
)
|
||||
|
||||
// Hidden debug option
|
||||
.addOption(
|
||||
new Option('--debug', 'debug mode')
|
||||
.hideHelp()
|
||||
)
|
||||
|
||||
.action((options) => {
|
||||
console.log(chalk.blue('Deployment Configuration:'));
|
||||
console.log('Environment:', chalk.yellow(options.env));
|
||||
console.log('Token:', options.token ? chalk.green('***set***') : chalk.red('missing'));
|
||||
console.log('Memory:', `${options.memory}MB`);
|
||||
console.log('Mode:', options.fast ? 'fast' : 'safe');
|
||||
console.log('Production Mode:', options.productionMode || false);
|
||||
console.log('Monitoring:', options.monitoring || false);
|
||||
|
||||
if (options.debug) {
|
||||
console.log(chalk.gray('\nDebug - All options:'));
|
||||
console.log(chalk.gray(JSON.stringify(options, null, 2)));
|
||||
}
|
||||
|
||||
console.log(chalk.green('\n✓ Deployment started'));
|
||||
});
|
||||
|
||||
program.parse();
|
||||
```
|
||||
|
||||
Usage:
|
||||
```bash
|
||||
export DEPLOY_TOKEN=abc123xyz789abc123xyz789abc12345
|
||||
|
||||
# Basic deployment
|
||||
deploy-cli deploy --env staging
|
||||
|
||||
# Custom memory
|
||||
deploy-cli deploy --env prod --memory 4G
|
||||
|
||||
# Fast mode
|
||||
deploy-cli deploy --env dev --fast
|
||||
|
||||
# Production mode (implies multiple settings)
|
||||
deploy-cli deploy --production-mode
|
||||
|
||||
# Debug
|
||||
deploy-cli deploy --env dev --debug
|
||||
```
|
||||
284
skills/commander-patterns/examples/basic-usage.md
Normal file
284
skills/commander-patterns/examples/basic-usage.md
Normal file
@@ -0,0 +1,284 @@
|
||||
# Basic Commander.js Usage
|
||||
|
||||
Simple examples demonstrating core Commander.js features.
|
||||
|
||||
## Example 1: Simple CLI with Commands
|
||||
|
||||
```typescript
|
||||
import { Command } from 'commander';
|
||||
|
||||
const program = new Command();
|
||||
|
||||
program
|
||||
.name('mycli')
|
||||
.description('My CLI tool')
|
||||
.version('1.0.0');
|
||||
|
||||
// Command without options
|
||||
program
|
||||
.command('init')
|
||||
.description('Initialize project')
|
||||
.action(() => {
|
||||
console.log('Initializing project...');
|
||||
});
|
||||
|
||||
// Command with options
|
||||
program
|
||||
.command('build')
|
||||
.description('Build project')
|
||||
.option('-w, --watch', 'watch mode')
|
||||
.action((options) => {
|
||||
console.log('Building...');
|
||||
if (options.watch) {
|
||||
console.log('Watch mode enabled');
|
||||
}
|
||||
});
|
||||
|
||||
program.parse();
|
||||
```
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
mycli init
|
||||
mycli build
|
||||
mycli build --watch
|
||||
mycli --help
|
||||
mycli --version
|
||||
```
|
||||
|
||||
## Example 2: Command with Arguments
|
||||
|
||||
```typescript
|
||||
import { Command } from 'commander';
|
||||
|
||||
const program = new Command();
|
||||
|
||||
program
|
||||
.name('mycli')
|
||||
.version('1.0.0');
|
||||
|
||||
program
|
||||
.command('greet <name>')
|
||||
.description('Greet someone')
|
||||
.action((name) => {
|
||||
console.log(`Hello, ${name}!`);
|
||||
});
|
||||
|
||||
program
|
||||
.command('add <a> <b>')
|
||||
.description('Add two numbers')
|
||||
.action((a, b) => {
|
||||
const sum = parseInt(a) + parseInt(b);
|
||||
console.log(`${a} + ${b} = ${sum}`);
|
||||
});
|
||||
|
||||
program.parse();
|
||||
```
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
mycli greet Alice
|
||||
# Output: Hello, Alice!
|
||||
|
||||
mycli add 5 3
|
||||
# Output: 5 + 3 = 8
|
||||
```
|
||||
|
||||
## Example 3: Options with Different Types
|
||||
|
||||
```typescript
|
||||
import { Command } from 'commander';
|
||||
|
||||
const program = new Command();
|
||||
|
||||
program
|
||||
.name('mycli')
|
||||
.version('1.0.0');
|
||||
|
||||
program
|
||||
.command('serve')
|
||||
.description('Start development server')
|
||||
// Boolean flag
|
||||
.option('-o, --open', 'open browser', false)
|
||||
// Option with value
|
||||
.option('-p, --port <port>', 'port number', '3000')
|
||||
// Option with default
|
||||
.option('-h, --host <host>', 'hostname', 'localhost')
|
||||
// Negatable option
|
||||
.option('--no-color', 'disable colors')
|
||||
.action((options) => {
|
||||
console.log(`Server running at http://${options.host}:${options.port}`);
|
||||
console.log('Open browser:', options.open);
|
||||
console.log('Colors:', options.color);
|
||||
});
|
||||
|
||||
program.parse();
|
||||
```
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
mycli serve
|
||||
# Uses defaults
|
||||
|
||||
mycli serve --port 8080
|
||||
# Uses custom port
|
||||
|
||||
mycli serve --open --no-color
|
||||
# Opens browser and disables colors
|
||||
```
|
||||
|
||||
## Example 4: Global Options
|
||||
|
||||
```typescript
|
||||
import { Command } from 'commander';
|
||||
|
||||
const program = new Command();
|
||||
|
||||
program
|
||||
.name('mycli')
|
||||
.version('1.0.0')
|
||||
// Global options
|
||||
.option('-v, --verbose', 'verbose output')
|
||||
.option('-c, --config <path>', 'config file path');
|
||||
|
||||
program
|
||||
.command('deploy')
|
||||
.action((options, command) => {
|
||||
const globalOpts = command.parent?.opts();
|
||||
console.log('Deploying...');
|
||||
if (globalOpts?.verbose) {
|
||||
console.log('Verbose mode enabled');
|
||||
console.log('Config:', globalOpts.config);
|
||||
}
|
||||
});
|
||||
|
||||
program.parse();
|
||||
```
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
mycli deploy
|
||||
# Normal output
|
||||
|
||||
mycli --verbose deploy
|
||||
# Verbose output
|
||||
|
||||
mycli --verbose --config ./config.json deploy
|
||||
# Verbose with custom config
|
||||
```
|
||||
|
||||
## Example 5: Multiple Commands
|
||||
|
||||
```typescript
|
||||
import { Command } from 'commander';
|
||||
import chalk from 'chalk';
|
||||
|
||||
const program = new Command();
|
||||
|
||||
program.name('mycli').version('1.0.0');
|
||||
|
||||
program
|
||||
.command('init')
|
||||
.description('Initialize new project')
|
||||
.action(() => {
|
||||
console.log(chalk.green('✓ Project initialized'));
|
||||
});
|
||||
|
||||
program
|
||||
.command('build')
|
||||
.description('Build project')
|
||||
.action(() => {
|
||||
console.log(chalk.blue('Building...'));
|
||||
console.log(chalk.green('✓ Build complete'));
|
||||
});
|
||||
|
||||
program
|
||||
.command('test')
|
||||
.description('Run tests')
|
||||
.action(() => {
|
||||
console.log(chalk.blue('Running tests...'));
|
||||
console.log(chalk.green('✓ All tests passed'));
|
||||
});
|
||||
|
||||
program
|
||||
.command('deploy')
|
||||
.description('Deploy to production')
|
||||
.action(() => {
|
||||
console.log(chalk.yellow('Deploying...'));
|
||||
console.log(chalk.green('✓ Deployed'));
|
||||
});
|
||||
|
||||
program.parse();
|
||||
```
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
mycli init
|
||||
mycli build
|
||||
mycli test
|
||||
mycli deploy
|
||||
mycli --help # Shows all commands
|
||||
```
|
||||
|
||||
## Running the Examples
|
||||
|
||||
### Setup
|
||||
|
||||
1. Create a new project:
|
||||
```bash
|
||||
mkdir my-cli
|
||||
cd my-cli
|
||||
npm init -y
|
||||
```
|
||||
|
||||
2. Install dependencies:
|
||||
```bash
|
||||
npm install commander chalk
|
||||
npm install -D typescript @types/node tsx
|
||||
```
|
||||
|
||||
3. Configure TypeScript:
|
||||
```bash
|
||||
npx tsc --init --target ES2022 --module ESNext
|
||||
```
|
||||
|
||||
4. Create `src/index.ts` with any example above
|
||||
|
||||
5. Run with tsx:
|
||||
```bash
|
||||
npx tsx src/index.ts --help
|
||||
```
|
||||
|
||||
### Building for Production
|
||||
|
||||
```bash
|
||||
# Build TypeScript
|
||||
npx tsc
|
||||
|
||||
# Run compiled version
|
||||
node dist/index.js
|
||||
```
|
||||
|
||||
### Making it Executable
|
||||
|
||||
Add to `package.json`:
|
||||
```json
|
||||
{
|
||||
"bin": {
|
||||
"mycli": "./dist/index.js"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Add shebang to top of source file:
|
||||
```typescript
|
||||
#!/usr/bin/env node
|
||||
import { Command } from 'commander';
|
||||
// ... rest of code
|
||||
```
|
||||
|
||||
Install globally for testing:
|
||||
```bash
|
||||
npm link
|
||||
mycli --help
|
||||
```
|
||||
558
skills/commander-patterns/examples/nested-commands-demo.md
Normal file
558
skills/commander-patterns/examples/nested-commands-demo.md
Normal file
@@ -0,0 +1,558 @@
|
||||
# Nested Commands Demo
|
||||
|
||||
Examples of building multi-level command hierarchies with Commander.js.
|
||||
|
||||
## Basic Nested Commands
|
||||
|
||||
```typescript
|
||||
import { Command } from 'commander';
|
||||
|
||||
const program = new Command();
|
||||
|
||||
program.name('mycli').version('1.0.0');
|
||||
|
||||
// Create parent command
|
||||
const config = program.command('config').description('Configuration management');
|
||||
|
||||
// Add subcommands
|
||||
config
|
||||
.command('get <key>')
|
||||
.description('Get config value')
|
||||
.action((key) => {
|
||||
console.log(`Getting ${key}...`);
|
||||
});
|
||||
|
||||
config
|
||||
.command('set <key> <value>')
|
||||
.description('Set config value')
|
||||
.action((key, value) => {
|
||||
console.log(`Setting ${key} = ${value}`);
|
||||
});
|
||||
|
||||
config
|
||||
.command('list')
|
||||
.description('List all config')
|
||||
.action(() => {
|
||||
console.log('Listing config...');
|
||||
});
|
||||
|
||||
program.parse();
|
||||
```
|
||||
|
||||
Usage:
|
||||
```bash
|
||||
mycli config get api-key
|
||||
mycli config set api-key abc123
|
||||
mycli config list
|
||||
mycli config --help # Shows subcommands
|
||||
```
|
||||
|
||||
## Multiple Command Groups
|
||||
|
||||
```typescript
|
||||
import { Command } from 'commander';
|
||||
import chalk from 'chalk';
|
||||
|
||||
const program = new Command();
|
||||
program.name('mycli').version('1.0.0');
|
||||
|
||||
// Database commands
|
||||
const db = program.command('db').description('Database operations');
|
||||
|
||||
db.command('migrate')
|
||||
.description('Run migrations')
|
||||
.option('-d, --dry-run', 'show migrations')
|
||||
.action((options) => {
|
||||
console.log(chalk.blue('Running migrations...'));
|
||||
});
|
||||
|
||||
db.command('seed')
|
||||
.description('Seed database')
|
||||
.option('-e, --env <env>', 'environment', 'dev')
|
||||
.action((options) => {
|
||||
console.log(chalk.blue(`Seeding ${options.env} database...`));
|
||||
});
|
||||
|
||||
db.command('reset')
|
||||
.description('Reset database')
|
||||
.option('-f, --force', 'skip confirmation')
|
||||
.action((options) => {
|
||||
if (!options.force) {
|
||||
console.log(chalk.red('Use --force to confirm'));
|
||||
return;
|
||||
}
|
||||
console.log(chalk.yellow('Resetting database...'));
|
||||
});
|
||||
|
||||
// User commands
|
||||
const user = program.command('user').description('User management');
|
||||
|
||||
user
|
||||
.command('list')
|
||||
.description('List users')
|
||||
.option('-p, --page <page>', 'page number', '1')
|
||||
.action((options) => {
|
||||
console.log(`Listing users (page ${options.page})`);
|
||||
});
|
||||
|
||||
user
|
||||
.command('create <username> <email>')
|
||||
.description('Create user')
|
||||
.action((username, email) => {
|
||||
console.log(chalk.green(`Created user: ${username} (${email})`));
|
||||
});
|
||||
|
||||
user
|
||||
.command('delete <userId>')
|
||||
.description('Delete user')
|
||||
.option('-f, --force', 'skip confirmation')
|
||||
.action((userId, options) => {
|
||||
console.log(chalk.red(`Deleted user: ${userId}`));
|
||||
});
|
||||
|
||||
// Cache commands
|
||||
const cache = program.command('cache').description('Cache management');
|
||||
|
||||
cache
|
||||
.command('clear')
|
||||
.description('Clear cache')
|
||||
.option('-a, --all', 'clear all caches')
|
||||
.action((options) => {
|
||||
console.log('Clearing cache...');
|
||||
});
|
||||
|
||||
cache
|
||||
.command('stats')
|
||||
.description('Show cache statistics')
|
||||
.action(() => {
|
||||
console.log('Cache stats:');
|
||||
console.log(' Size: 1.2 GB');
|
||||
console.log(' Hits: 89%');
|
||||
});
|
||||
|
||||
program.parse();
|
||||
```
|
||||
|
||||
Usage:
|
||||
```bash
|
||||
mycli db migrate --dry-run
|
||||
mycli db seed --env prod
|
||||
mycli db reset --force
|
||||
|
||||
mycli user list --page 2
|
||||
mycli user create john john@example.com
|
||||
mycli user delete 123 --force
|
||||
|
||||
mycli cache clear --all
|
||||
mycli cache stats
|
||||
```
|
||||
|
||||
## Three-Level Nesting
|
||||
|
||||
```typescript
|
||||
import { Command } from 'commander';
|
||||
|
||||
const program = new Command();
|
||||
program.name('mycli').version('1.0.0');
|
||||
|
||||
// Level 1: Cloud
|
||||
const cloud = program.command('cloud').description('Cloud operations');
|
||||
|
||||
// Level 2: AWS
|
||||
const aws = cloud.command('aws').description('AWS operations');
|
||||
|
||||
// Level 3: EC2
|
||||
const ec2 = aws.command('ec2').description('EC2 operations');
|
||||
|
||||
ec2
|
||||
.command('list')
|
||||
.description('List EC2 instances')
|
||||
.action(() => {
|
||||
console.log('Listing EC2 instances...');
|
||||
});
|
||||
|
||||
ec2
|
||||
.command('start <instanceId>')
|
||||
.description('Start EC2 instance')
|
||||
.action((instanceId) => {
|
||||
console.log(`Starting instance ${instanceId}...`);
|
||||
});
|
||||
|
||||
ec2
|
||||
.command('stop <instanceId>')
|
||||
.description('Stop EC2 instance')
|
||||
.action((instanceId) => {
|
||||
console.log(`Stopping instance ${instanceId}...`);
|
||||
});
|
||||
|
||||
// Level 3: S3
|
||||
const s3 = aws.command('s3').description('S3 operations');
|
||||
|
||||
s3.command('list')
|
||||
.description('List S3 buckets')
|
||||
.action(() => {
|
||||
console.log('Listing S3 buckets...');
|
||||
});
|
||||
|
||||
s3.command('upload <file> <bucket>')
|
||||
.description('Upload to S3')
|
||||
.action((file, bucket) => {
|
||||
console.log(`Uploading ${file} to ${bucket}...`);
|
||||
});
|
||||
|
||||
// Level 2: Azure
|
||||
const azure = cloud.command('azure').description('Azure operations');
|
||||
|
||||
azure
|
||||
.command('vm-list')
|
||||
.description('List VMs')
|
||||
.action(() => {
|
||||
console.log('Listing Azure VMs...');
|
||||
});
|
||||
|
||||
program.parse();
|
||||
```
|
||||
|
||||
Usage:
|
||||
```bash
|
||||
mycli cloud aws ec2 list
|
||||
mycli cloud aws ec2 start i-123456
|
||||
mycli cloud aws s3 list
|
||||
mycli cloud aws s3 upload file.txt my-bucket
|
||||
mycli cloud azure vm-list
|
||||
```
|
||||
|
||||
## CRUD Pattern
|
||||
|
||||
```typescript
|
||||
import { Command } from 'commander';
|
||||
import chalk from 'chalk';
|
||||
|
||||
const program = new Command();
|
||||
program.name('api-cli').version('1.0.0');
|
||||
|
||||
function createResourceCommands(name: string) {
|
||||
const resource = program.command(name).description(`${name} management`);
|
||||
|
||||
resource
|
||||
.command('list')
|
||||
.description(`List all ${name}`)
|
||||
.option('-p, --page <page>', 'page number', '1')
|
||||
.option('-l, --limit <limit>', 'items per page', '10')
|
||||
.action((options) => {
|
||||
console.log(chalk.blue(`Listing ${name}...`));
|
||||
console.log(`Page ${options.page}, Limit ${options.limit}`);
|
||||
});
|
||||
|
||||
resource
|
||||
.command('get <id>')
|
||||
.description(`Get ${name} by ID`)
|
||||
.action((id) => {
|
||||
console.log(chalk.blue(`Getting ${name} ${id}...`));
|
||||
});
|
||||
|
||||
resource
|
||||
.command('create')
|
||||
.description(`Create new ${name}`)
|
||||
.option('-d, --data <json>', 'JSON data')
|
||||
.action((options) => {
|
||||
console.log(chalk.green(`Creating ${name}...`));
|
||||
console.log('Data:', options.data);
|
||||
});
|
||||
|
||||
resource
|
||||
.command('update <id>')
|
||||
.description(`Update ${name}`)
|
||||
.option('-d, --data <json>', 'JSON data')
|
||||
.action((id, options) => {
|
||||
console.log(chalk.yellow(`Updating ${name} ${id}...`));
|
||||
console.log('Data:', options.data);
|
||||
});
|
||||
|
||||
resource
|
||||
.command('delete <id>')
|
||||
.description(`Delete ${name}`)
|
||||
.option('-f, --force', 'skip confirmation')
|
||||
.action((id, options) => {
|
||||
if (!options.force) {
|
||||
console.log(chalk.red('Use --force to confirm deletion'));
|
||||
return;
|
||||
}
|
||||
console.log(chalk.red(`Deleted ${name} ${id}`));
|
||||
});
|
||||
|
||||
return resource;
|
||||
}
|
||||
|
||||
// Create CRUD commands for different resources
|
||||
createResourceCommands('users');
|
||||
createResourceCommands('posts');
|
||||
createResourceCommands('comments');
|
||||
|
||||
program.parse();
|
||||
```
|
||||
|
||||
Usage:
|
||||
```bash
|
||||
# Users
|
||||
api-cli users list --page 2
|
||||
api-cli users get 123
|
||||
api-cli users create --data '{"name":"John"}'
|
||||
api-cli users update 123 --data '{"email":"new@example.com"}'
|
||||
api-cli users delete 123 --force
|
||||
|
||||
# Posts
|
||||
api-cli posts list
|
||||
api-cli posts get 456
|
||||
api-cli posts create --data '{"title":"Hello"}'
|
||||
|
||||
# Comments
|
||||
api-cli comments list
|
||||
```
|
||||
|
||||
## Modular Command Structure
|
||||
|
||||
Split commands into separate files for better organization:
|
||||
|
||||
**src/commands/config.ts**
|
||||
```typescript
|
||||
import { Command } from 'commander';
|
||||
|
||||
export function createConfigCommand(): Command {
|
||||
const config = new Command('config').description('Configuration management');
|
||||
|
||||
config
|
||||
.command('get <key>')
|
||||
.description('Get config value')
|
||||
.action((key) => {
|
||||
console.log(`Getting ${key}...`);
|
||||
});
|
||||
|
||||
config
|
||||
.command('set <key> <value>')
|
||||
.description('Set config value')
|
||||
.action((key, value) => {
|
||||
console.log(`Setting ${key} = ${value}`);
|
||||
});
|
||||
|
||||
return config;
|
||||
}
|
||||
```
|
||||
|
||||
**src/commands/user.ts**
|
||||
```typescript
|
||||
import { Command } from 'commander';
|
||||
|
||||
export function createUserCommand(): Command {
|
||||
const user = new Command('user').description('User management');
|
||||
|
||||
user
|
||||
.command('list')
|
||||
.description('List users')
|
||||
.action(() => {
|
||||
console.log('Listing users...');
|
||||
});
|
||||
|
||||
user
|
||||
.command('create <username>')
|
||||
.description('Create user')
|
||||
.action((username) => {
|
||||
console.log(`Creating user: ${username}`);
|
||||
});
|
||||
|
||||
return user;
|
||||
}
|
||||
```
|
||||
|
||||
**src/index.ts**
|
||||
```typescript
|
||||
import { Command } from 'commander';
|
||||
import { createConfigCommand } from './commands/config';
|
||||
import { createUserCommand } from './commands/user';
|
||||
|
||||
const program = new Command();
|
||||
|
||||
program
|
||||
.name('mycli')
|
||||
.version('1.0.0')
|
||||
.description('My CLI tool');
|
||||
|
||||
// Add command groups
|
||||
program.addCommand(createConfigCommand());
|
||||
program.addCommand(createUserCommand());
|
||||
|
||||
program.parse();
|
||||
```
|
||||
|
||||
## Nested Commands with Shared Options
|
||||
|
||||
```typescript
|
||||
import { Command } from 'commander';
|
||||
|
||||
const program = new Command();
|
||||
program.name('deploy-cli').version('1.0.0');
|
||||
|
||||
// Parent deploy command with shared options
|
||||
const deploy = program
|
||||
.command('deploy')
|
||||
.description('Deployment operations')
|
||||
.option('-e, --env <environment>', 'environment', 'dev')
|
||||
.option('-v, --verbose', 'verbose output');
|
||||
|
||||
// Subcommands inherit parent context
|
||||
deploy
|
||||
.command('start')
|
||||
.description('Start deployment')
|
||||
.option('-f, --force', 'force deployment')
|
||||
.action((options, command) => {
|
||||
const parentOpts = command.parent?.opts();
|
||||
console.log('Environment:', parentOpts?.env);
|
||||
console.log('Verbose:', parentOpts?.verbose);
|
||||
console.log('Force:', options.force);
|
||||
});
|
||||
|
||||
deploy
|
||||
.command('status')
|
||||
.description('Check deployment status')
|
||||
.action((options, command) => {
|
||||
const parentOpts = command.parent?.opts();
|
||||
console.log(`Checking ${parentOpts?.env} status...`);
|
||||
});
|
||||
|
||||
deploy
|
||||
.command('rollback [version]')
|
||||
.description('Rollback deployment')
|
||||
.action((version, options, command) => {
|
||||
const parentOpts = command.parent?.opts();
|
||||
console.log(`Rolling back ${parentOpts?.env}...`);
|
||||
});
|
||||
|
||||
program.parse();
|
||||
```
|
||||
|
||||
Usage:
|
||||
```bash
|
||||
deploy-cli deploy --env prod start --force
|
||||
deploy-cli deploy --env staging status
|
||||
deploy-cli deploy --verbose rollback v1.0.0
|
||||
```
|
||||
|
||||
## Command Aliases
|
||||
|
||||
```typescript
|
||||
import { Command } from 'commander';
|
||||
|
||||
const program = new Command();
|
||||
program.name('mycli').version('1.0.0');
|
||||
|
||||
const db = program.command('database').alias('db').description('Database ops');
|
||||
|
||||
db.command('migrate')
|
||||
.alias('m')
|
||||
.description('Run migrations')
|
||||
.action(() => {
|
||||
console.log('Running migrations...');
|
||||
});
|
||||
|
||||
db.command('seed')
|
||||
.alias('s')
|
||||
.description('Seed database')
|
||||
.action(() => {
|
||||
console.log('Seeding...');
|
||||
});
|
||||
|
||||
program.parse();
|
||||
```
|
||||
|
||||
Usage (all equivalent):
|
||||
```bash
|
||||
mycli database migrate
|
||||
mycli db migrate
|
||||
mycli db m
|
||||
|
||||
mycli database seed
|
||||
mycli db seed
|
||||
mycli db s
|
||||
```
|
||||
|
||||
## Complete CLI Example
|
||||
|
||||
```typescript
|
||||
#!/usr/bin/env node
|
||||
import { Command } from 'commander';
|
||||
import chalk from 'chalk';
|
||||
|
||||
const program = new Command();
|
||||
|
||||
program
|
||||
.name('project-cli')
|
||||
.version('1.0.0')
|
||||
.description('Project management CLI');
|
||||
|
||||
// Project commands
|
||||
const project = program.command('project').alias('p').description('Project operations');
|
||||
|
||||
project
|
||||
.command('init <name>')
|
||||
.description('Initialize new project')
|
||||
.option('-t, --template <type>', 'template', 'basic')
|
||||
.action((name, options) => {
|
||||
console.log(chalk.green(`✓ Initialized ${name} with ${options.template} template`));
|
||||
});
|
||||
|
||||
project
|
||||
.command('build')
|
||||
.description('Build project')
|
||||
.action(() => {
|
||||
console.log(chalk.blue('Building...'));
|
||||
});
|
||||
|
||||
// Environment commands
|
||||
const env = program.command('env').alias('e').description('Environment management');
|
||||
|
||||
env
|
||||
.command('list')
|
||||
.alias('ls')
|
||||
.description('List environments')
|
||||
.action(() => {
|
||||
console.log('dev, staging, prod');
|
||||
});
|
||||
|
||||
env
|
||||
.command('create <name>')
|
||||
.description('Create environment')
|
||||
.action((name) => {
|
||||
console.log(chalk.green(`✓ Created environment: ${name}`));
|
||||
});
|
||||
|
||||
// Deploy commands
|
||||
const deploy = program.command('deploy').alias('d').description('Deployment operations');
|
||||
|
||||
deploy
|
||||
.command('start <env>')
|
||||
.description('Start deployment')
|
||||
.action((env) => {
|
||||
console.log(chalk.blue(`Deploying to ${env}...`));
|
||||
});
|
||||
|
||||
deploy
|
||||
.command('status [env]')
|
||||
.description('Check status')
|
||||
.action((env) => {
|
||||
console.log(`Status of ${env || 'all'}:`);
|
||||
});
|
||||
|
||||
program.parse();
|
||||
```
|
||||
|
||||
Usage:
|
||||
```bash
|
||||
project-cli project init my-app --template advanced
|
||||
project-cli p build
|
||||
|
||||
project-cli env list
|
||||
project-cli e create staging
|
||||
|
||||
project-cli deploy start prod
|
||||
project-cli d status
|
||||
```
|
||||
406
skills/commander-patterns/examples/options-arguments-demo.md
Normal file
406
skills/commander-patterns/examples/options-arguments-demo.md
Normal file
@@ -0,0 +1,406 @@
|
||||
# Commander.js Options and Arguments Demo
|
||||
|
||||
Comprehensive examples of option and argument patterns.
|
||||
|
||||
## Option Patterns
|
||||
|
||||
### 1. Boolean Flags
|
||||
|
||||
```typescript
|
||||
program
|
||||
.command('build')
|
||||
.option('-w, --watch', 'watch for changes')
|
||||
.option('-m, --minify', 'minify output')
|
||||
.option('-v, --verbose', 'verbose logging')
|
||||
.action((options) => {
|
||||
console.log('Watch:', options.watch); // true or undefined
|
||||
console.log('Minify:', options.minify);
|
||||
console.log('Verbose:', options.verbose);
|
||||
});
|
||||
```
|
||||
|
||||
Usage:
|
||||
```bash
|
||||
mycli build --watch --minify
|
||||
```
|
||||
|
||||
### 2. Options with Required Values
|
||||
|
||||
```typescript
|
||||
program
|
||||
.command('deploy')
|
||||
.option('-e, --env <environment>', 'deployment environment')
|
||||
.option('-r, --region <region>', 'AWS region')
|
||||
.action((options) => {
|
||||
console.log('Environment:', options.env);
|
||||
console.log('Region:', options.region);
|
||||
});
|
||||
```
|
||||
|
||||
Usage:
|
||||
```bash
|
||||
mycli deploy --env production --region us-west-2
|
||||
```
|
||||
|
||||
### 3. Options with Optional Values
|
||||
|
||||
```typescript
|
||||
program
|
||||
.command('log')
|
||||
.option('-f, --file [path]', 'log file path')
|
||||
.action((options) => {
|
||||
if (options.file === true) {
|
||||
console.log('Using default log file');
|
||||
} else if (options.file) {
|
||||
console.log('Log file:', options.file);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
Usage:
|
||||
```bash
|
||||
mycli log --file # file = true
|
||||
mycli log --file custom.log # file = 'custom.log'
|
||||
mycli log # file = undefined
|
||||
```
|
||||
|
||||
### 4. Options with Defaults
|
||||
|
||||
```typescript
|
||||
program
|
||||
.command('serve')
|
||||
.option('-p, --port <port>', 'port number', '3000')
|
||||
.option('-h, --host <host>', 'hostname', 'localhost')
|
||||
.action((options) => {
|
||||
console.log(`http://${options.host}:${options.port}`);
|
||||
});
|
||||
```
|
||||
|
||||
Usage:
|
||||
```bash
|
||||
mycli serve # Uses defaults
|
||||
mycli serve --port 8080 # Custom port
|
||||
```
|
||||
|
||||
### 5. Negatable Options
|
||||
|
||||
```typescript
|
||||
program
|
||||
.command('build')
|
||||
.option('--cache', 'enable cache')
|
||||
.option('--no-cache', 'disable cache')
|
||||
.option('--color', 'enable colors')
|
||||
.option('--no-color', 'disable colors')
|
||||
.action((options) => {
|
||||
console.log('Cache:', options.cache); // undefined, true, or false
|
||||
console.log('Color:', options.color);
|
||||
});
|
||||
```
|
||||
|
||||
Usage:
|
||||
```bash
|
||||
mycli build # cache & color = undefined
|
||||
mycli build --cache # cache = true
|
||||
mycli build --no-cache # cache = false
|
||||
mycli build --no-color # color = false
|
||||
```
|
||||
|
||||
### 6. Variadic Options
|
||||
|
||||
```typescript
|
||||
program
|
||||
.command('tag')
|
||||
.option('-t, --tags <tags...>', 'multiple tags')
|
||||
.action((options) => {
|
||||
console.log('Tags:', options.tags); // Array
|
||||
});
|
||||
```
|
||||
|
||||
Usage:
|
||||
```bash
|
||||
mycli tag --tags v1.0 production stable
|
||||
# Tags: ['v1.0', 'production', 'stable']
|
||||
```
|
||||
|
||||
### 7. Options with Custom Parsers
|
||||
|
||||
```typescript
|
||||
program
|
||||
.command('scale')
|
||||
.option('-r, --replicas <count>', 'replica count', parseInt)
|
||||
.option('-m, --memory <mb>', 'memory in MB', parseFloat)
|
||||
.option('-t, --timeout <sec>', 'timeout', (value) => {
|
||||
return parseInt(value) * 1000; // Convert to ms
|
||||
})
|
||||
.action((options) => {
|
||||
console.log('Replicas:', options.replicas); // Number
|
||||
console.log('Memory:', options.memory); // Number
|
||||
console.log('Timeout:', options.timeout); // Number (ms)
|
||||
});
|
||||
```
|
||||
|
||||
Usage:
|
||||
```bash
|
||||
mycli scale --replicas 5 --memory 512.5 --timeout 30
|
||||
```
|
||||
|
||||
## Argument Patterns
|
||||
|
||||
### 1. Required Arguments
|
||||
|
||||
```typescript
|
||||
program
|
||||
.command('deploy <environment>')
|
||||
.description('Deploy to environment')
|
||||
.action((environment) => {
|
||||
console.log('Deploying to:', environment);
|
||||
});
|
||||
```
|
||||
|
||||
Usage:
|
||||
```bash
|
||||
mycli deploy production # ✓ Works
|
||||
mycli deploy # ✗ Error: missing required argument
|
||||
```
|
||||
|
||||
### 2. Optional Arguments
|
||||
|
||||
```typescript
|
||||
program
|
||||
.command('create <name> [description]')
|
||||
.description('Create item')
|
||||
.action((name, description) => {
|
||||
console.log('Name:', name);
|
||||
console.log('Description:', description || 'No description');
|
||||
});
|
||||
```
|
||||
|
||||
Usage:
|
||||
```bash
|
||||
mycli create "My Item"
|
||||
mycli create "My Item" "A detailed description"
|
||||
```
|
||||
|
||||
### 3. Variadic Arguments
|
||||
|
||||
```typescript
|
||||
program
|
||||
.command('add <items...>')
|
||||
.description('Add multiple items')
|
||||
.action((items) => {
|
||||
console.log('Items:', items); // Array
|
||||
items.forEach((item, i) => {
|
||||
console.log(` ${i + 1}. ${item}`);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
Usage:
|
||||
```bash
|
||||
mycli add item1 item2 item3
|
||||
# Items: ['item1', 'item2', 'item3']
|
||||
```
|
||||
|
||||
### 4. Mixed Required and Optional
|
||||
|
||||
```typescript
|
||||
program
|
||||
.command('copy <source> <destination> [options]')
|
||||
.description('Copy files')
|
||||
.action((source, destination, options) => {
|
||||
console.log('Source:', source);
|
||||
console.log('Destination:', destination);
|
||||
console.log('Options:', options || 'none');
|
||||
});
|
||||
```
|
||||
|
||||
### 5. Arguments with Descriptions
|
||||
|
||||
```typescript
|
||||
program
|
||||
.command('deploy <environment>')
|
||||
.argument('<environment>', 'target environment (dev, staging, prod)')
|
||||
.argument('[region]', 'deployment region', 'us-east-1')
|
||||
.action((environment, region) => {
|
||||
console.log(`Deploying to ${environment} in ${region}`);
|
||||
});
|
||||
```
|
||||
|
||||
### 6. Arguments with Custom Parsers
|
||||
|
||||
```typescript
|
||||
program
|
||||
.command('wait <seconds>')
|
||||
.argument('<seconds>', 'seconds to wait', parseFloat)
|
||||
.action(async (seconds) => {
|
||||
console.log(`Waiting ${seconds} seconds...`);
|
||||
await new Promise((resolve) => setTimeout(resolve, seconds * 1000));
|
||||
console.log('Done!');
|
||||
});
|
||||
```
|
||||
|
||||
Usage:
|
||||
```bash
|
||||
mycli wait 2.5 # Waits 2.5 seconds
|
||||
```
|
||||
|
||||
### 7. Arguments with Validation
|
||||
|
||||
```typescript
|
||||
program
|
||||
.command('set-port <port>')
|
||||
.argument('<port>', 'port number', (value) => {
|
||||
const port = parseInt(value, 10);
|
||||
if (isNaN(port)) {
|
||||
throw new Error('Port must be a number');
|
||||
}
|
||||
if (port < 1 || port > 65535) {
|
||||
throw new Error('Port must be between 1 and 65535');
|
||||
}
|
||||
return port;
|
||||
})
|
||||
.action((port) => {
|
||||
console.log('Port set to:', port);
|
||||
});
|
||||
```
|
||||
|
||||
## Combined Patterns
|
||||
|
||||
### Arguments + Options
|
||||
|
||||
```typescript
|
||||
program
|
||||
.command('deploy <environment>')
|
||||
.argument('<environment>', 'deployment environment')
|
||||
.option('-f, --force', 'force deployment')
|
||||
.option('-d, --dry-run', 'simulate deployment')
|
||||
.option('-t, --tag <tag>', 'deployment tag', 'latest')
|
||||
.action((environment, options) => {
|
||||
console.log('Environment:', environment);
|
||||
console.log('Force:', options.force);
|
||||
console.log('Dry run:', options.dryRun);
|
||||
console.log('Tag:', options.tag);
|
||||
});
|
||||
```
|
||||
|
||||
Usage:
|
||||
```bash
|
||||
mycli deploy production --force --tag v1.2.3
|
||||
```
|
||||
|
||||
### Variadic Arguments + Options
|
||||
|
||||
```typescript
|
||||
program
|
||||
.command('compile <files...>')
|
||||
.argument('<files...>', 'files to compile')
|
||||
.option('-o, --output <dir>', 'output directory', './dist')
|
||||
.option('-m, --minify', 'minify output')
|
||||
.action((files, options) => {
|
||||
console.log('Files:', files);
|
||||
console.log('Output:', options.output);
|
||||
console.log('Minify:', options.minify);
|
||||
});
|
||||
```
|
||||
|
||||
Usage:
|
||||
```bash
|
||||
mycli compile src/a.ts src/b.ts --output build --minify
|
||||
```
|
||||
|
||||
### Multiple Option Types
|
||||
|
||||
```typescript
|
||||
program
|
||||
.command('start')
|
||||
.option('-p, --port <port>', 'port', '3000')
|
||||
.option('-h, --host <host>', 'host', 'localhost')
|
||||
.option('-o, --open', 'open browser')
|
||||
.option('--no-color', 'disable colors')
|
||||
.option('-e, --env <vars...>', 'environment variables')
|
||||
.action((options) => {
|
||||
console.log('Server:', `http://${options.host}:${options.port}`);
|
||||
console.log('Open:', options.open);
|
||||
console.log('Color:', options.color);
|
||||
console.log('Env:', options.env);
|
||||
});
|
||||
```
|
||||
|
||||
Usage:
|
||||
```bash
|
||||
mycli start --port 8080 --open --env NODE_ENV=production DEBUG=true
|
||||
```
|
||||
|
||||
## Complete Example
|
||||
|
||||
```typescript
|
||||
#!/usr/bin/env node
|
||||
import { Command } from 'commander';
|
||||
import chalk from 'chalk';
|
||||
|
||||
const program = new Command();
|
||||
|
||||
program.name('deploy-cli').version('1.0.0');
|
||||
|
||||
program
|
||||
.command('deploy <environment> [region]')
|
||||
.description('Deploy application')
|
||||
.argument('<environment>', 'target environment (dev, staging, prod)')
|
||||
.argument('[region]', 'deployment region', 'us-east-1')
|
||||
.option('-f, --force', 'skip confirmations', false)
|
||||
.option('-d, --dry-run', 'simulate deployment', false)
|
||||
.option('-t, --tag <tag>', 'deployment tag', 'latest')
|
||||
.option('-r, --replicas <count>', 'replica count', parseInt, 3)
|
||||
.option('--env <vars...>', 'environment variables')
|
||||
.option('--no-rollback', 'disable auto-rollback')
|
||||
.action((environment, region, options) => {
|
||||
console.log(chalk.blue('Deployment Configuration:'));
|
||||
console.log('Environment:', chalk.yellow(environment));
|
||||
console.log('Region:', region);
|
||||
console.log('Tag:', options.tag);
|
||||
console.log('Replicas:', options.replicas);
|
||||
console.log('Force:', options.force);
|
||||
console.log('Dry run:', options.dryRun);
|
||||
console.log('Rollback:', options.rollback);
|
||||
|
||||
if (options.env) {
|
||||
console.log('Environment variables:');
|
||||
options.env.forEach((v) => console.log(` ${v}`));
|
||||
}
|
||||
|
||||
if (options.dryRun) {
|
||||
console.log(chalk.yellow('\n🔍 Dry run - no actual deployment'));
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(chalk.green('\n✓ Deployment complete!'));
|
||||
});
|
||||
|
||||
program.parse();
|
||||
```
|
||||
|
||||
Usage examples:
|
||||
```bash
|
||||
# Basic
|
||||
deploy-cli deploy production
|
||||
|
||||
# With region
|
||||
deploy-cli deploy staging us-west-2
|
||||
|
||||
# With options
|
||||
deploy-cli deploy prod --force --replicas 5
|
||||
|
||||
# With environment variables
|
||||
deploy-cli deploy prod --env NODE_ENV=production API_KEY=xyz
|
||||
|
||||
# Dry run
|
||||
deploy-cli deploy prod --dry-run --no-rollback
|
||||
|
||||
# All together
|
||||
deploy-cli deploy production us-west-2 \
|
||||
--force \
|
||||
--tag v2.0.0 \
|
||||
--replicas 10 \
|
||||
--env NODE_ENV=production DEBUG=false \
|
||||
--no-rollback
|
||||
```
|
||||
67
skills/commander-patterns/scripts/extract-command-help.sh
Executable file
67
skills/commander-patterns/scripts/extract-command-help.sh
Executable file
@@ -0,0 +1,67 @@
|
||||
#!/bin/bash
|
||||
# Extract help text from Commander.js CLI for documentation
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
CLI_COMMAND="${1:?Usage: $0 <cli-command> [output-file]}"
|
||||
OUTPUT_FILE="${2:-docs/CLI-REFERENCE.md}"
|
||||
|
||||
echo "📚 Extracting help documentation from: $CLI_COMMAND"
|
||||
|
||||
# Create output directory
|
||||
mkdir -p "$(dirname "$OUTPUT_FILE")"
|
||||
|
||||
# Start markdown file
|
||||
cat > "$OUTPUT_FILE" << EOF
|
||||
# CLI Reference
|
||||
|
||||
Auto-generated documentation for $CLI_COMMAND
|
||||
|
||||
---
|
||||
|
||||
## Main Command
|
||||
|
||||
\`\`\`
|
||||
EOF
|
||||
|
||||
# Get main help
|
||||
$CLI_COMMAND --help >> "$OUTPUT_FILE" || true
|
||||
|
||||
cat >> "$OUTPUT_FILE" << 'EOF'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Commands
|
||||
|
||||
EOF
|
||||
|
||||
# Extract all commands
|
||||
COMMANDS=$($CLI_COMMAND --help | grep -A 100 "Commands:" | tail -n +2 | awk '{print $1}' | grep -v "^$" || echo "")
|
||||
|
||||
if [[ -n "$COMMANDS" ]]; then
|
||||
for cmd in $COMMANDS; do
|
||||
echo "### \`$cmd\`" >> "$OUTPUT_FILE"
|
||||
echo "" >> "$OUTPUT_FILE"
|
||||
echo "\`\`\`" >> "$OUTPUT_FILE"
|
||||
$CLI_COMMAND $cmd --help >> "$OUTPUT_FILE" 2>&1 || true
|
||||
echo "\`\`\`" >> "$OUTPUT_FILE"
|
||||
echo "" >> "$OUTPUT_FILE"
|
||||
done
|
||||
else
|
||||
echo "No subcommands found." >> "$OUTPUT_FILE"
|
||||
fi
|
||||
|
||||
cat >> "$OUTPUT_FILE" << EOF
|
||||
|
||||
---
|
||||
|
||||
*Generated: $(date)*
|
||||
*CLI Version: $($CLI_COMMAND --version 2>/dev/null || echo "unknown")*
|
||||
EOF
|
||||
|
||||
echo "✅ Documentation generated: $OUTPUT_FILE"
|
||||
echo ""
|
||||
echo "Preview:"
|
||||
head -n 20 "$OUTPUT_FILE"
|
||||
echo "..."
|
||||
59
skills/commander-patterns/scripts/generate-command.sh
Executable file
59
skills/commander-patterns/scripts/generate-command.sh
Executable file
@@ -0,0 +1,59 @@
|
||||
#!/bin/bash
|
||||
# Generate Commander.js command with options and arguments
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
COMMAND_NAME="${1:?Usage: $0 <command-name> [output-file]}"
|
||||
OUTPUT_FILE="${2:-src/commands/${COMMAND_NAME}.ts}"
|
||||
|
||||
# Create output directory
|
||||
mkdir -p "$(dirname "$OUTPUT_FILE")"
|
||||
|
||||
echo "📝 Generating Commander.js command: $COMMAND_NAME"
|
||||
|
||||
cat > "$OUTPUT_FILE" << 'EOF'
|
||||
import { Command, Option } from 'commander';
|
||||
import chalk from 'chalk';
|
||||
|
||||
export function create{{COMMAND_NAME_PASCAL}}Command(): Command {
|
||||
const command = new Command('{{COMMAND_NAME}}')
|
||||
.description('{{DESCRIPTION}}')
|
||||
.option('-v, --verbose', 'verbose output', false)
|
||||
.addOption(
|
||||
new Option('-e, --environment <env>', 'target environment')
|
||||
.choices(['dev', 'staging', 'prod'])
|
||||
.default('dev')
|
||||
)
|
||||
.action(async (options) => {
|
||||
try {
|
||||
console.log(chalk.blue(`Running {{COMMAND_NAME}} command...`));
|
||||
console.log('Options:', options);
|
||||
|
||||
// TODO: Implement command logic
|
||||
|
||||
console.log(chalk.green('✓ Command completed successfully'));
|
||||
} catch (error) {
|
||||
console.error(chalk.red('✗ Command failed:'), error.message);
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
|
||||
return command;
|
||||
}
|
||||
EOF
|
||||
|
||||
# Convert command name to PascalCase
|
||||
COMMAND_NAME_PASCAL=$(echo "$COMMAND_NAME" | sed -r 's/(^|-)([a-z])/\U\2/g')
|
||||
|
||||
# Replace placeholders
|
||||
sed -i "s/{{COMMAND_NAME}}/$COMMAND_NAME/g" "$OUTPUT_FILE"
|
||||
sed -i "s/{{COMMAND_NAME_PASCAL}}/$COMMAND_NAME_PASCAL/g" "$OUTPUT_FILE"
|
||||
sed -i "s/{{DESCRIPTION}}/Execute $COMMAND_NAME operation/g" "$OUTPUT_FILE"
|
||||
|
||||
echo "✅ Command generated: $OUTPUT_FILE"
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo " 1. Implement command logic in the action handler"
|
||||
echo " 2. Add custom options and arguments as needed"
|
||||
echo " 3. Import and add to main CLI: program.addCommand(create${COMMAND_NAME_PASCAL}Command())"
|
||||
echo ""
|
||||
75
skills/commander-patterns/scripts/generate-subcommand.sh
Executable file
75
skills/commander-patterns/scripts/generate-subcommand.sh
Executable file
@@ -0,0 +1,75 @@
|
||||
#!/bin/bash
|
||||
# Generate nested subcommand structure for Commander.js
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
PARENT_COMMAND="${1:?Usage: $0 <parent-command> <subcommand> [output-file]}"
|
||||
SUBCOMMAND="${2:?Usage: $0 <parent-command> <subcommand> [output-file]}"
|
||||
OUTPUT_FILE="${3:-src/commands/${PARENT_COMMAND}/${SUBCOMMAND}.ts}"
|
||||
|
||||
# Create output directory
|
||||
mkdir -p "$(dirname "$OUTPUT_FILE")"
|
||||
|
||||
echo "📝 Generating subcommand: $PARENT_COMMAND $SUBCOMMAND"
|
||||
|
||||
cat > "$OUTPUT_FILE" << 'EOF'
|
||||
import { Command } from 'commander';
|
||||
import chalk from 'chalk';
|
||||
|
||||
export function create{{SUBCOMMAND_PASCAL}}Command(): Command {
|
||||
const command = new Command('{{SUBCOMMAND}}')
|
||||
.description('{{DESCRIPTION}}')
|
||||
.action(async (options) => {
|
||||
try {
|
||||
console.log(chalk.blue(`Running {{PARENT_COMMAND}} {{SUBCOMMAND}}...`));
|
||||
|
||||
// TODO: Implement subcommand logic
|
||||
|
||||
console.log(chalk.green('✓ Subcommand completed'));
|
||||
} catch (error) {
|
||||
console.error(chalk.red('✗ Subcommand failed:'), error.message);
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
|
||||
return command;
|
||||
}
|
||||
EOF
|
||||
|
||||
# Convert to PascalCase
|
||||
SUBCOMMAND_PASCAL=$(echo "$SUBCOMMAND" | sed -r 's/(^|-)([a-z])/\U\2/g')
|
||||
|
||||
# Replace placeholders
|
||||
sed -i "s/{{PARENT_COMMAND}}/$PARENT_COMMAND/g" "$OUTPUT_FILE"
|
||||
sed -i "s/{{SUBCOMMAND}}/$SUBCOMMAND/g" "$OUTPUT_FILE"
|
||||
sed -i "s/{{SUBCOMMAND_PASCAL}}/$SUBCOMMAND_PASCAL/g" "$OUTPUT_FILE"
|
||||
sed -i "s/{{DESCRIPTION}}/$SUBCOMMAND operation for $PARENT_COMMAND/g" "$OUTPUT_FILE"
|
||||
|
||||
# Generate parent command file if it doesn't exist
|
||||
PARENT_FILE="src/commands/${PARENT_COMMAND}/index.ts"
|
||||
if [[ ! -f "$PARENT_FILE" ]]; then
|
||||
mkdir -p "$(dirname "$PARENT_FILE")"
|
||||
cat > "$PARENT_FILE" << EOF
|
||||
import { Command } from 'commander';
|
||||
import { create${SUBCOMMAND_PASCAL}Command } from './${SUBCOMMAND}';
|
||||
|
||||
export function create${PARENT_COMMAND^}Command(): Command {
|
||||
const command = new Command('${PARENT_COMMAND}')
|
||||
.description('${PARENT_COMMAND^} operations');
|
||||
|
||||
// Add subcommands
|
||||
command.addCommand(create${SUBCOMMAND_PASCAL}Command());
|
||||
|
||||
return command;
|
||||
}
|
||||
EOF
|
||||
echo "✅ Created parent command: $PARENT_FILE"
|
||||
fi
|
||||
|
||||
echo "✅ Subcommand generated: $OUTPUT_FILE"
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo " 1. Implement subcommand logic"
|
||||
echo " 2. Add to parent command: ${PARENT_FILE}"
|
||||
echo " 3. Import parent in main CLI: program.addCommand(create${PARENT_COMMAND^}Command())"
|
||||
echo ""
|
||||
83
skills/commander-patterns/scripts/test-commander-cli.sh
Executable file
83
skills/commander-patterns/scripts/test-commander-cli.sh
Executable file
@@ -0,0 +1,83 @@
|
||||
#!/bin/bash
|
||||
# Test Commander.js CLI with various inputs
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
CLI_COMMAND="${1:?Usage: $0 <cli-command> [test-suite]}"
|
||||
TEST_SUITE="${2:-basic}"
|
||||
|
||||
echo "🧪 Testing Commander.js CLI: $CLI_COMMAND"
|
||||
echo "Test suite: $TEST_SUITE"
|
||||
echo ""
|
||||
|
||||
PASSED=0
|
||||
FAILED=0
|
||||
|
||||
# Helper function to run test
|
||||
run_test() {
|
||||
local test_name="$1"
|
||||
local test_command="$2"
|
||||
local expected_exit_code="${3:-0}"
|
||||
|
||||
echo -n "Testing: $test_name ... "
|
||||
|
||||
if eval "$test_command" > /dev/null 2>&1; then
|
||||
actual_exit_code=0
|
||||
else
|
||||
actual_exit_code=$?
|
||||
fi
|
||||
|
||||
if [[ $actual_exit_code -eq $expected_exit_code ]]; then
|
||||
echo "✅ PASS"
|
||||
((PASSED++))
|
||||
else
|
||||
echo "❌ FAIL (expected exit code $expected_exit_code, got $actual_exit_code)"
|
||||
((FAILED++))
|
||||
fi
|
||||
}
|
||||
|
||||
# Basic tests
|
||||
if [[ "$TEST_SUITE" == "basic" || "$TEST_SUITE" == "all" ]]; then
|
||||
echo "Running basic tests..."
|
||||
run_test "Help flag" "$CLI_COMMAND --help" 0
|
||||
run_test "Version flag" "$CLI_COMMAND --version" 0
|
||||
run_test "No arguments" "$CLI_COMMAND" 1
|
||||
fi
|
||||
|
||||
# Command tests
|
||||
if [[ "$TEST_SUITE" == "commands" || "$TEST_SUITE" == "all" ]]; then
|
||||
echo ""
|
||||
echo "Running command tests..."
|
||||
run_test "List commands" "$CLI_COMMAND --help | grep -q 'Commands:'" 0
|
||||
fi
|
||||
|
||||
# Option tests
|
||||
if [[ "$TEST_SUITE" == "options" || "$TEST_SUITE" == "all" ]]; then
|
||||
echo ""
|
||||
echo "Running option tests..."
|
||||
run_test "Unknown option" "$CLI_COMMAND --unknown-option" 1
|
||||
run_test "Short flag" "$CLI_COMMAND -v" 0
|
||||
run_test "Long flag" "$CLI_COMMAND --verbose" 0
|
||||
fi
|
||||
|
||||
# Argument tests
|
||||
if [[ "$TEST_SUITE" == "arguments" || "$TEST_SUITE" == "all" ]]; then
|
||||
echo ""
|
||||
echo "Running argument tests..."
|
||||
run_test "Required argument missing" "$CLI_COMMAND deploy" 1
|
||||
run_test "Required argument provided" "$CLI_COMMAND deploy production" 0
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Test Results:"
|
||||
echo " Passed: $PASSED"
|
||||
echo " Failed: $FAILED"
|
||||
echo ""
|
||||
|
||||
if [[ $FAILED -gt 0 ]]; then
|
||||
echo "❌ Some tests failed"
|
||||
exit 1
|
||||
else
|
||||
echo "✅ All tests passed!"
|
||||
exit 0
|
||||
fi
|
||||
139
skills/commander-patterns/scripts/validate-commander-structure.sh
Executable file
139
skills/commander-patterns/scripts/validate-commander-structure.sh
Executable file
@@ -0,0 +1,139 @@
|
||||
#!/bin/bash
|
||||
# Validate Commander.js CLI structure and patterns
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
CLI_FILE="${1:?Usage: $0 <cli-file.ts|js>}"
|
||||
|
||||
if [[ ! -f "$CLI_FILE" ]]; then
|
||||
echo "Error: File not found: $CLI_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "🔍 Validating Commander.js CLI structure: $CLI_FILE"
|
||||
echo ""
|
||||
|
||||
ERRORS=0
|
||||
WARNINGS=0
|
||||
|
||||
# Check for Commander import
|
||||
if grep -q "from 'commander'" "$CLI_FILE" || grep -q 'require("commander")' "$CLI_FILE"; then
|
||||
echo "✅ Commander.js import found"
|
||||
else
|
||||
echo "❌ Missing Commander.js import"
|
||||
((ERRORS++))
|
||||
fi
|
||||
|
||||
# Check for Command instantiation
|
||||
if grep -q "new Command()" "$CLI_FILE" || grep -q "= program" "$CLI_FILE"; then
|
||||
echo "✅ Command instance created"
|
||||
else
|
||||
echo "❌ No Command instance found"
|
||||
((ERRORS++))
|
||||
fi
|
||||
|
||||
# Check for program.parse()
|
||||
if grep -q "\.parse()" "$CLI_FILE"; then
|
||||
echo "✅ program.parse() called"
|
||||
else
|
||||
echo "❌ Missing program.parse() call"
|
||||
((ERRORS++))
|
||||
fi
|
||||
|
||||
# Check for .name()
|
||||
if grep -q "\.name(" "$CLI_FILE"; then
|
||||
echo "✅ CLI name defined"
|
||||
else
|
||||
echo "⚠️ CLI name not set (recommended)"
|
||||
((WARNINGS++))
|
||||
fi
|
||||
|
||||
# Check for .description()
|
||||
if grep -q "\.description(" "$CLI_FILE"; then
|
||||
echo "✅ CLI description defined"
|
||||
else
|
||||
echo "⚠️ CLI description not set (recommended)"
|
||||
((WARNINGS++))
|
||||
fi
|
||||
|
||||
# Check for .version()
|
||||
if grep -q "\.version(" "$CLI_FILE"; then
|
||||
echo "✅ CLI version defined"
|
||||
else
|
||||
echo "⚠️ CLI version not set (recommended)"
|
||||
((WARNINGS++))
|
||||
fi
|
||||
|
||||
# Check for commands
|
||||
COMMAND_COUNT=$(grep -c "\.command(" "$CLI_FILE" || echo "0")
|
||||
if [[ $COMMAND_COUNT -gt 0 ]]; then
|
||||
echo "✅ Found $COMMAND_COUNT command(s)"
|
||||
else
|
||||
echo "⚠️ No commands defined"
|
||||
((WARNINGS++))
|
||||
fi
|
||||
|
||||
# Check for action handlers
|
||||
ACTION_COUNT=$(grep -c "\.action(" "$CLI_FILE" || echo "0")
|
||||
if [[ $ACTION_COUNT -gt 0 ]]; then
|
||||
echo "✅ Found $ACTION_COUNT action handler(s)"
|
||||
else
|
||||
echo "⚠️ No action handlers defined"
|
||||
((WARNINGS++))
|
||||
fi
|
||||
|
||||
# Check for options
|
||||
OPTION_COUNT=$(grep -c "\.option(" "$CLI_FILE" || echo "0")
|
||||
ADDOPTION_COUNT=$(grep -c "\.addOption(" "$CLI_FILE" || echo "0")
|
||||
TOTAL_OPTIONS=$((OPTION_COUNT + ADDOPTION_COUNT))
|
||||
if [[ $TOTAL_OPTIONS -gt 0 ]]; then
|
||||
echo "✅ Found $TOTAL_OPTIONS option(s)"
|
||||
else
|
||||
echo "⚠️ No options defined"
|
||||
((WARNINGS++))
|
||||
fi
|
||||
|
||||
# Check for arguments
|
||||
ARGUMENT_COUNT=$(grep -c "\.argument(" "$CLI_FILE" || echo "0")
|
||||
if [[ $ARGUMENT_COUNT -gt 0 ]]; then
|
||||
echo "✅ Found $ARGUMENT_COUNT argument(s)"
|
||||
fi
|
||||
|
||||
# Check for Option class usage
|
||||
if grep -q "new Option(" "$CLI_FILE"; then
|
||||
echo "✅ Option class used (advanced)"
|
||||
fi
|
||||
|
||||
# Check for error handling
|
||||
if grep -q "try\|catch" "$CLI_FILE" || grep -q "\.exitOverride()" "$CLI_FILE"; then
|
||||
echo "✅ Error handling present"
|
||||
else
|
||||
echo "⚠️ No error handling detected (recommended)"
|
||||
((WARNINGS++))
|
||||
fi
|
||||
|
||||
# Check for TypeScript
|
||||
if [[ "$CLI_FILE" == *.ts ]]; then
|
||||
echo "✅ TypeScript file"
|
||||
# Check for types
|
||||
if grep -q "import.*Command.*Option.*from 'commander'" "$CLI_FILE"; then
|
||||
echo "✅ Proper TypeScript imports"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Summary:"
|
||||
echo " Errors: $ERRORS"
|
||||
echo " Warnings: $WARNINGS"
|
||||
echo ""
|
||||
|
||||
if [[ $ERRORS -gt 0 ]]; then
|
||||
echo "❌ Validation failed with $ERRORS error(s)"
|
||||
exit 1
|
||||
elif [[ $WARNINGS -gt 0 ]]; then
|
||||
echo "⚠️ Validation passed with $WARNINGS warning(s)"
|
||||
exit 0
|
||||
else
|
||||
echo "✅ Validation passed - excellent CLI structure!"
|
||||
exit 0
|
||||
fi
|
||||
19
skills/commander-patterns/templates/basic-commander.js
Normal file
19
skills/commander-patterns/templates/basic-commander.js
Normal file
@@ -0,0 +1,19 @@
|
||||
#!/usr/bin/env node
|
||||
import { Command } from 'commander';
|
||||
|
||||
const program = new Command();
|
||||
|
||||
program
|
||||
.name('mycli')
|
||||
.description('A simple CLI tool')
|
||||
.version('1.0.0');
|
||||
|
||||
program
|
||||
.command('hello')
|
||||
.description('Say hello')
|
||||
.option('-n, --name <name>', 'name to greet', 'World')
|
||||
.action((options) => {
|
||||
console.log(`Hello, ${options.name}!`);
|
||||
});
|
||||
|
||||
program.parse();
|
||||
18
skills/commander-patterns/templates/basic-commander.ts
Normal file
18
skills/commander-patterns/templates/basic-commander.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Command } from 'commander';
|
||||
|
||||
const program = new Command();
|
||||
|
||||
program
|
||||
.name('mycli')
|
||||
.description('A simple CLI tool')
|
||||
.version('1.0.0');
|
||||
|
||||
program
|
||||
.command('hello')
|
||||
.description('Say hello')
|
||||
.option('-n, --name <name>', 'name to greet', 'World')
|
||||
.action((options) => {
|
||||
console.log(`Hello, ${options.name}!`);
|
||||
});
|
||||
|
||||
program.parse();
|
||||
@@ -0,0 +1,93 @@
|
||||
import { Command } from 'commander';
|
||||
import chalk from 'chalk';
|
||||
|
||||
const program = new Command();
|
||||
|
||||
program
|
||||
.name('mycli')
|
||||
.description('CLI with various argument types')
|
||||
.version('1.0.0');
|
||||
|
||||
// Command with required argument
|
||||
program
|
||||
.command('deploy <environment>')
|
||||
.description('Deploy to specified environment')
|
||||
.argument('<environment>', 'target environment (dev, staging, prod)')
|
||||
.action((environment) => {
|
||||
console.log(chalk.blue(`Deploying to ${environment}...`));
|
||||
});
|
||||
|
||||
// Command with required and optional arguments
|
||||
program
|
||||
.command('create <name> [description]')
|
||||
.description('Create new item')
|
||||
.argument('<name>', 'item name')
|
||||
.argument('[description]', 'item description', 'No description provided')
|
||||
.action((name, description) => {
|
||||
console.log(`Creating: ${name}`);
|
||||
console.log(`Description: ${description}`);
|
||||
});
|
||||
|
||||
// Command with variadic arguments
|
||||
program
|
||||
.command('add <items...>')
|
||||
.description('Add multiple items')
|
||||
.argument('<items...>', 'items to add')
|
||||
.action((items) => {
|
||||
console.log(chalk.blue('Adding items:'));
|
||||
items.forEach((item, index) => {
|
||||
console.log(` ${index + 1}. ${item}`);
|
||||
});
|
||||
console.log(chalk.green(`✓ Added ${items.length} items`));
|
||||
});
|
||||
|
||||
// Command with custom argument parser
|
||||
program
|
||||
.command('wait <seconds>')
|
||||
.description('Wait for specified time')
|
||||
.argument('<seconds>', 'seconds to wait', parseFloat)
|
||||
.action(async (seconds) => {
|
||||
console.log(chalk.blue(`Waiting ${seconds} seconds...`));
|
||||
await new Promise((resolve) => setTimeout(resolve, seconds * 1000));
|
||||
console.log(chalk.green('✓ Done'));
|
||||
});
|
||||
|
||||
// Command with arguments and options
|
||||
program
|
||||
.command('copy <source> <destination>')
|
||||
.description('Copy file from source to destination')
|
||||
.argument('<source>', 'source file path')
|
||||
.argument('<destination>', 'destination file path')
|
||||
.option('-f, --force', 'overwrite if exists', false)
|
||||
.option('-r, --recursive', 'copy recursively', false)
|
||||
.action((source, destination, options) => {
|
||||
console.log(`Copying ${source} to ${destination}`);
|
||||
console.log('Options:', options);
|
||||
if (options.force) {
|
||||
console.log(chalk.yellow('⚠ Force mode: will overwrite existing files'));
|
||||
}
|
||||
if (options.recursive) {
|
||||
console.log(chalk.blue('Recursive copy enabled'));
|
||||
}
|
||||
console.log(chalk.green('✓ Copy complete'));
|
||||
});
|
||||
|
||||
// Command with argument validation
|
||||
program
|
||||
.command('set-port <port>')
|
||||
.description('Set application port')
|
||||
.argument('<port>', 'port number', (value) => {
|
||||
const port = parseInt(value, 10);
|
||||
if (isNaN(port)) {
|
||||
throw new Error('Port must be a number');
|
||||
}
|
||||
if (port < 1 || port > 65535) {
|
||||
throw new Error('Port must be between 1 and 65535');
|
||||
}
|
||||
return port;
|
||||
})
|
||||
.action((port) => {
|
||||
console.log(chalk.green(`✓ Port set to ${port}`));
|
||||
});
|
||||
|
||||
program.parse();
|
||||
60
skills/commander-patterns/templates/command-with-options.ts
Normal file
60
skills/commander-patterns/templates/command-with-options.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import { Command, Option } from 'commander';
|
||||
import chalk from 'chalk';
|
||||
|
||||
const program = new Command();
|
||||
|
||||
program
|
||||
.name('mycli')
|
||||
.description('CLI with various option types')
|
||||
.version('1.0.0');
|
||||
|
||||
program
|
||||
.command('deploy')
|
||||
.description('Deploy application')
|
||||
// Boolean flag
|
||||
.option('-v, --verbose', 'verbose output', false)
|
||||
// Option with required value
|
||||
.option('-p, --port <port>', 'port number', '3000')
|
||||
// Option with optional value
|
||||
.option('-c, --config [path]', 'config file path')
|
||||
// Negatable option
|
||||
.option('--no-build', 'skip build step')
|
||||
// Multiple choice option using Option class
|
||||
.addOption(
|
||||
new Option('-e, --env <environment>', 'target environment')
|
||||
.choices(['dev', 'staging', 'prod'])
|
||||
.default('dev')
|
||||
)
|
||||
// Option with custom parser
|
||||
.addOption(
|
||||
new Option('-r, --replicas <count>', 'number of replicas')
|
||||
.argParser(parseInt)
|
||||
.default(3)
|
||||
)
|
||||
// Mandatory option
|
||||
.addOption(
|
||||
new Option('-t, --token <token>', 'API token')
|
||||
.makeOptionMandatory()
|
||||
.env('API_TOKEN')
|
||||
)
|
||||
// Variadic option
|
||||
.option('--tags <tags...>', 'deployment tags')
|
||||
.action((options) => {
|
||||
console.log(chalk.blue('Deploying with options:'));
|
||||
console.log('Verbose:', options.verbose);
|
||||
console.log('Port:', options.port);
|
||||
console.log('Config:', options.config);
|
||||
console.log('Build:', options.build);
|
||||
console.log('Environment:', options.env);
|
||||
console.log('Replicas:', options.replicas);
|
||||
console.log('Token:', options.token ? '***' : 'not set');
|
||||
console.log('Tags:', options.tags);
|
||||
|
||||
if (options.verbose) {
|
||||
console.log(chalk.gray('Verbose mode enabled'));
|
||||
}
|
||||
|
||||
console.log(chalk.green('✓ Deployment complete'));
|
||||
});
|
||||
|
||||
program.parse();
|
||||
341
skills/commander-patterns/templates/commander-with-inquirer.ts
Normal file
341
skills/commander-patterns/templates/commander-with-inquirer.ts
Normal file
@@ -0,0 +1,341 @@
|
||||
import { Command } from 'commander';
|
||||
import inquirer from 'inquirer';
|
||||
import chalk from 'chalk';
|
||||
|
||||
const program = new Command();
|
||||
|
||||
program
|
||||
.name('mycli')
|
||||
.description('Interactive CLI with Inquirer.js integration')
|
||||
.version('1.0.0');
|
||||
|
||||
// Interactive init command
|
||||
program
|
||||
.command('init')
|
||||
.description('Initialize project interactively')
|
||||
.option('-y, --yes', 'skip prompts and use defaults', false)
|
||||
.action(async (options) => {
|
||||
if (options.yes) {
|
||||
console.log(chalk.blue('Using default configuration...'));
|
||||
await initProject({
|
||||
name: 'my-project',
|
||||
template: 'basic',
|
||||
features: [],
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Interactive prompts
|
||||
const answers = await inquirer.prompt([
|
||||
{
|
||||
type: 'input',
|
||||
name: 'name',
|
||||
message: 'Project name:',
|
||||
default: 'my-project',
|
||||
validate: (input) => {
|
||||
if (!input.trim()) {
|
||||
return 'Project name is required';
|
||||
}
|
||||
if (!/^[a-z0-9-]+$/.test(input)) {
|
||||
return 'Project name must contain only lowercase letters, numbers, and hyphens';
|
||||
}
|
||||
return true;
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
name: 'template',
|
||||
message: 'Choose a template:',
|
||||
choices: ['basic', 'advanced', 'enterprise'],
|
||||
default: 'basic',
|
||||
},
|
||||
{
|
||||
type: 'checkbox',
|
||||
name: 'features',
|
||||
message: 'Select features:',
|
||||
choices: [
|
||||
{ name: 'TypeScript', value: 'typescript', checked: true },
|
||||
{ name: 'ESLint', value: 'eslint', checked: true },
|
||||
{ name: 'Prettier', value: 'prettier', checked: true },
|
||||
{ name: 'Testing (Jest)', value: 'jest' },
|
||||
{ name: 'CI/CD', value: 'cicd' },
|
||||
{ name: 'Docker', value: 'docker' },
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'install',
|
||||
message: 'Install dependencies now?',
|
||||
default: true,
|
||||
},
|
||||
]);
|
||||
|
||||
await initProject(answers);
|
||||
});
|
||||
|
||||
// Interactive deploy command
|
||||
program
|
||||
.command('deploy')
|
||||
.description('Deploy with interactive configuration')
|
||||
.option('-e, --env <environment>', 'skip environment prompt')
|
||||
.action(async (options) => {
|
||||
const questions: any[] = [];
|
||||
|
||||
// Conditionally add environment question
|
||||
if (!options.env) {
|
||||
questions.push({
|
||||
type: 'list',
|
||||
name: 'environment',
|
||||
message: 'Select deployment environment:',
|
||||
choices: ['dev', 'staging', 'prod'],
|
||||
});
|
||||
}
|
||||
|
||||
questions.push(
|
||||
{
|
||||
type: 'list',
|
||||
name: 'strategy',
|
||||
message: 'Deployment strategy:',
|
||||
choices: ['rolling', 'blue-green', 'canary'],
|
||||
default: 'rolling',
|
||||
},
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'runTests',
|
||||
message: 'Run tests before deployment?',
|
||||
default: true,
|
||||
},
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'createBackup',
|
||||
message: 'Create backup before deployment?',
|
||||
default: true,
|
||||
when: (answers) => answers.environment === 'prod',
|
||||
},
|
||||
{
|
||||
type: 'password',
|
||||
name: 'token',
|
||||
message: 'Enter deployment token:',
|
||||
mask: '*',
|
||||
validate: (input) => (input.length > 0 ? true : 'Token is required'),
|
||||
}
|
||||
);
|
||||
|
||||
const answers = await inquirer.prompt(questions);
|
||||
|
||||
const deployConfig = {
|
||||
environment: options.env || answers.environment,
|
||||
...answers,
|
||||
};
|
||||
|
||||
console.log(chalk.blue('\nDeployment configuration:'));
|
||||
console.log(JSON.stringify(deployConfig, null, 2));
|
||||
|
||||
const { confirm } = await inquirer.prompt([
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'confirm',
|
||||
message: 'Proceed with deployment?',
|
||||
default: false,
|
||||
},
|
||||
]);
|
||||
|
||||
if (!confirm) {
|
||||
console.log(chalk.yellow('Deployment cancelled'));
|
||||
return;
|
||||
}
|
||||
|
||||
await deploy(deployConfig);
|
||||
});
|
||||
|
||||
// Interactive config command
|
||||
program
|
||||
.command('config')
|
||||
.description('Configure application interactively')
|
||||
.action(async () => {
|
||||
const mainMenu = await inquirer.prompt([
|
||||
{
|
||||
type: 'list',
|
||||
name: 'action',
|
||||
message: 'What would you like to do?',
|
||||
choices: [
|
||||
{ name: 'View configuration', value: 'view' },
|
||||
{ name: 'Edit configuration', value: 'edit' },
|
||||
{ name: 'Reset to defaults', value: 'reset' },
|
||||
{ name: 'Exit', value: 'exit' },
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
switch (mainMenu.action) {
|
||||
case 'view':
|
||||
console.log(chalk.blue('\nCurrent configuration:'));
|
||||
console.log(JSON.stringify(getConfig(), null, 2));
|
||||
break;
|
||||
|
||||
case 'edit':
|
||||
const config = await inquirer.prompt([
|
||||
{
|
||||
type: 'input',
|
||||
name: 'apiUrl',
|
||||
message: 'API URL:',
|
||||
default: getConfig().apiUrl,
|
||||
},
|
||||
{
|
||||
type: 'number',
|
||||
name: 'timeout',
|
||||
message: 'Request timeout (ms):',
|
||||
default: getConfig().timeout,
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
name: 'logLevel',
|
||||
message: 'Log level:',
|
||||
choices: ['debug', 'info', 'warn', 'error'],
|
||||
default: getConfig().logLevel,
|
||||
},
|
||||
]);
|
||||
saveConfig(config);
|
||||
console.log(chalk.green('✓ Configuration saved'));
|
||||
break;
|
||||
|
||||
case 'reset':
|
||||
const { confirmReset } = await inquirer.prompt([
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'confirmReset',
|
||||
message: 'Reset to default configuration?',
|
||||
default: false,
|
||||
},
|
||||
]);
|
||||
if (confirmReset) {
|
||||
resetConfig();
|
||||
console.log(chalk.green('✓ Configuration reset'));
|
||||
}
|
||||
break;
|
||||
|
||||
case 'exit':
|
||||
console.log(chalk.gray('Goodbye!'));
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
// Multi-step wizard
|
||||
program
|
||||
.command('wizard')
|
||||
.description('Run setup wizard')
|
||||
.action(async () => {
|
||||
console.log(chalk.blue('🧙 Welcome to the setup wizard\n'));
|
||||
|
||||
// Step 1: Basic info
|
||||
console.log(chalk.bold('Step 1: Basic Information'));
|
||||
const step1 = await inquirer.prompt([
|
||||
{
|
||||
type: 'input',
|
||||
name: 'projectName',
|
||||
message: 'Project name:',
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'description',
|
||||
message: 'Description:',
|
||||
},
|
||||
]);
|
||||
|
||||
// Step 2: Technology stack
|
||||
console.log(chalk.bold('\nStep 2: Technology Stack'));
|
||||
const step2 = await inquirer.prompt([
|
||||
{
|
||||
type: 'list',
|
||||
name: 'language',
|
||||
message: 'Primary language:',
|
||||
choices: ['TypeScript', 'JavaScript', 'Python', 'Go', 'Rust'],
|
||||
},
|
||||
{
|
||||
type: 'checkbox',
|
||||
name: 'frameworks',
|
||||
message: 'Select frameworks:',
|
||||
choices: (answers) => {
|
||||
const frameworksByLanguage: Record<string, string[]> = {
|
||||
TypeScript: ['Next.js', 'Express', 'NestJS'],
|
||||
JavaScript: ['React', 'Vue', 'Express'],
|
||||
Python: ['FastAPI', 'Django', 'Flask'],
|
||||
Go: ['Gin', 'Echo', 'Fiber'],
|
||||
Rust: ['Actix', 'Rocket', 'Axum'],
|
||||
};
|
||||
return frameworksByLanguage[answers.language] || [];
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
// Step 3: Infrastructure
|
||||
console.log(chalk.bold('\nStep 3: Infrastructure'));
|
||||
const step3 = await inquirer.prompt([
|
||||
{
|
||||
type: 'list',
|
||||
name: 'database',
|
||||
message: 'Database:',
|
||||
choices: ['PostgreSQL', 'MySQL', 'MongoDB', 'SQLite', 'None'],
|
||||
},
|
||||
{
|
||||
type: 'checkbox',
|
||||
name: 'services',
|
||||
message: 'Additional services:',
|
||||
choices: ['Redis', 'ElasticSearch', 'RabbitMQ', 'S3'],
|
||||
},
|
||||
]);
|
||||
|
||||
// Summary
|
||||
const config = { ...step1, ...step2, ...step3 };
|
||||
|
||||
console.log(chalk.bold('\n📋 Configuration Summary:'));
|
||||
console.log(JSON.stringify(config, null, 2));
|
||||
|
||||
const { confirm } = await inquirer.prompt([
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'confirm',
|
||||
message: 'Create project with this configuration?',
|
||||
default: true,
|
||||
},
|
||||
]);
|
||||
|
||||
if (confirm) {
|
||||
console.log(chalk.green('\n✓ Project created successfully!'));
|
||||
} else {
|
||||
console.log(chalk.yellow('\n✗ Project creation cancelled'));
|
||||
}
|
||||
});
|
||||
|
||||
// Helper functions
|
||||
async function initProject(config: any) {
|
||||
console.log(chalk.blue('\nInitializing project...'));
|
||||
console.log('Name:', config.name);
|
||||
console.log('Template:', config.template);
|
||||
console.log('Features:', config.features.join(', '));
|
||||
console.log(chalk.green('\n✓ Project initialized!'));
|
||||
}
|
||||
|
||||
async function deploy(config: any) {
|
||||
console.log(chalk.blue('\nDeploying...'));
|
||||
console.log(JSON.stringify(config, null, 2));
|
||||
console.log(chalk.green('\n✓ Deployment complete!'));
|
||||
}
|
||||
|
||||
function getConfig() {
|
||||
return {
|
||||
apiUrl: 'https://api.example.com',
|
||||
timeout: 5000,
|
||||
logLevel: 'info',
|
||||
};
|
||||
}
|
||||
|
||||
function saveConfig(config: any) {
|
||||
console.log('Saving config:', config);
|
||||
}
|
||||
|
||||
function resetConfig() {
|
||||
console.log('Resetting config to defaults');
|
||||
}
|
||||
|
||||
program.parse();
|
||||
266
skills/commander-patterns/templates/commander-with-validation.ts
Normal file
266
skills/commander-patterns/templates/commander-with-validation.ts
Normal file
@@ -0,0 +1,266 @@
|
||||
import { Command, Option } from 'commander';
|
||||
import chalk from 'chalk';
|
||||
import { z } from 'zod';
|
||||
|
||||
const program = new Command();
|
||||
|
||||
program
|
||||
.name('mycli')
|
||||
.description('CLI with comprehensive input validation')
|
||||
.version('1.0.0');
|
||||
|
||||
// Zod schemas for validation
|
||||
const EmailSchema = z.string().email();
|
||||
const UrlSchema = z.string().url();
|
||||
const PortSchema = z.number().int().min(1).max(65535);
|
||||
const EnvironmentSchema = z.enum(['dev', 'staging', 'prod']);
|
||||
|
||||
// Validation helper
|
||||
function validateOrThrow<T>(schema: z.ZodSchema<T>, value: unknown, fieldName: string): T {
|
||||
try {
|
||||
return schema.parse(value);
|
||||
} catch (error) {
|
||||
if (error instanceof z.ZodError) {
|
||||
throw new Error(`Invalid ${fieldName}: ${error.errors[0].message}`);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Command with validated arguments
|
||||
program
|
||||
.command('create-user <email> <username>')
|
||||
.description('Create user with validated inputs')
|
||||
.argument('<email>', 'user email', (value) => {
|
||||
return validateOrThrow(EmailSchema, value, 'email');
|
||||
})
|
||||
.argument('<username>', 'username', (value) => {
|
||||
const UsernameSchema = z
|
||||
.string()
|
||||
.min(3, 'Username must be at least 3 characters')
|
||||
.max(20, 'Username must be at most 20 characters')
|
||||
.regex(/^[a-z0-9_]+$/, 'Username must contain only lowercase letters, numbers, and underscores');
|
||||
|
||||
return validateOrThrow(UsernameSchema, value, 'username');
|
||||
})
|
||||
.option('-a, --age <age>', 'user age', (value) => {
|
||||
const age = parseInt(value, 10);
|
||||
const AgeSchema = z.number().int().min(13).max(120);
|
||||
return validateOrThrow(AgeSchema, age, 'age');
|
||||
})
|
||||
.action((email, username, options) => {
|
||||
console.log(chalk.green('✓ User validation passed'));
|
||||
console.log('Email:', email);
|
||||
console.log('Username:', username);
|
||||
if (options.age) {
|
||||
console.log('Age:', options.age);
|
||||
}
|
||||
});
|
||||
|
||||
// Command with validated options
|
||||
program
|
||||
.command('deploy')
|
||||
.description('Deploy with validated configuration')
|
||||
.addOption(
|
||||
new Option('-e, --env <environment>', 'deployment environment')
|
||||
.argParser((value) => {
|
||||
return validateOrThrow(EnvironmentSchema, value, 'environment');
|
||||
})
|
||||
.makeOptionMandatory()
|
||||
)
|
||||
.addOption(
|
||||
new Option('-u, --url <url>', 'deployment URL')
|
||||
.argParser((value) => {
|
||||
return validateOrThrow(UrlSchema, value, 'URL');
|
||||
})
|
||||
.makeOptionMandatory()
|
||||
)
|
||||
.addOption(
|
||||
new Option('-p, --port <port>', 'server port')
|
||||
.argParser((value) => {
|
||||
const port = parseInt(value, 10);
|
||||
return validateOrThrow(PortSchema, port, 'port');
|
||||
})
|
||||
.default(3000)
|
||||
)
|
||||
.addOption(
|
||||
new Option('-r, --replicas <count>', 'replica count')
|
||||
.argParser((value) => {
|
||||
const count = parseInt(value, 10);
|
||||
const ReplicaSchema = z.number().int().min(1).max(100);
|
||||
return validateOrThrow(ReplicaSchema, count, 'replicas');
|
||||
})
|
||||
.default(3)
|
||||
)
|
||||
.action((options) => {
|
||||
console.log(chalk.green('✓ Deployment configuration validated'));
|
||||
console.log('Environment:', options.env);
|
||||
console.log('URL:', options.url);
|
||||
console.log('Port:', options.port);
|
||||
console.log('Replicas:', options.replicas);
|
||||
});
|
||||
|
||||
// Command with complex object validation
|
||||
program
|
||||
.command('configure')
|
||||
.description('Configure application with JSON validation')
|
||||
.option('-c, --config <json>', 'configuration as JSON string', (value) => {
|
||||
// Parse JSON
|
||||
let parsed;
|
||||
try {
|
||||
parsed = JSON.parse(value);
|
||||
} catch {
|
||||
throw new Error('Invalid JSON format');
|
||||
}
|
||||
|
||||
// Validate with Zod schema
|
||||
const ConfigSchema = z.object({
|
||||
api: z.object({
|
||||
url: z.string().url(),
|
||||
timeout: z.number().int().positive(),
|
||||
retries: z.number().int().min(0).max(5),
|
||||
}),
|
||||
database: z.object({
|
||||
host: z.string(),
|
||||
port: z.number().int().min(1).max(65535),
|
||||
name: z.string(),
|
||||
}),
|
||||
features: z.object({
|
||||
enableCache: z.boolean(),
|
||||
enableMetrics: z.boolean(),
|
||||
}),
|
||||
});
|
||||
|
||||
return validateOrThrow(ConfigSchema, parsed, 'configuration');
|
||||
})
|
||||
.action((options) => {
|
||||
if (options.config) {
|
||||
console.log(chalk.green('✓ Configuration validated'));
|
||||
console.log(JSON.stringify(options.config, null, 2));
|
||||
}
|
||||
});
|
||||
|
||||
// Command with file path validation
|
||||
program
|
||||
.command('process <inputFile> <outputFile>')
|
||||
.description('Process file with path validation')
|
||||
.argument('<inputFile>', 'input file path', (value) => {
|
||||
const FilePathSchema = z.string().refine(
|
||||
(path) => {
|
||||
// Check file extension
|
||||
return path.endsWith('.json') || path.endsWith('.yaml') || path.endsWith('.yml');
|
||||
},
|
||||
{ message: 'File must be .json, .yaml, or .yml' }
|
||||
);
|
||||
|
||||
return validateOrThrow(FilePathSchema, value, 'input file');
|
||||
})
|
||||
.argument('<outputFile>', 'output file path', (value) => {
|
||||
const FilePathSchema = z.string().min(1);
|
||||
return validateOrThrow(FilePathSchema, value, 'output file');
|
||||
})
|
||||
.action((inputFile, outputFile) => {
|
||||
console.log(chalk.green('✓ File paths validated'));
|
||||
console.log('Input:', inputFile);
|
||||
console.log('Output:', outputFile);
|
||||
});
|
||||
|
||||
// Command with date/time validation
|
||||
program
|
||||
.command('schedule <datetime>')
|
||||
.description('Schedule task with datetime validation')
|
||||
.argument('<datetime>', 'ISO 8601 datetime', (value) => {
|
||||
const date = new Date(value);
|
||||
|
||||
if (isNaN(date.getTime())) {
|
||||
throw new Error('Invalid datetime format (use ISO 8601)');
|
||||
}
|
||||
|
||||
if (date < new Date()) {
|
||||
throw new Error('Datetime must be in the future');
|
||||
}
|
||||
|
||||
return date;
|
||||
})
|
||||
.option('-d, --duration <minutes>', 'duration in minutes', (value) => {
|
||||
const minutes = parseInt(value, 10);
|
||||
const DurationSchema = z.number().int().min(1).max(1440); // 1 min to 24 hours
|
||||
return validateOrThrow(DurationSchema, minutes, 'duration');
|
||||
})
|
||||
.action((datetime, options) => {
|
||||
console.log(chalk.green('✓ Schedule validated'));
|
||||
console.log('Start time:', datetime.toISOString());
|
||||
if (options.duration) {
|
||||
const endTime = new Date(datetime.getTime() + options.duration * 60000);
|
||||
console.log('End time:', endTime.toISOString());
|
||||
}
|
||||
});
|
||||
|
||||
// Command with array validation
|
||||
program
|
||||
.command('tag <items...>')
|
||||
.description('Tag items with validation')
|
||||
.argument('<items...>', 'items to tag')
|
||||
.option('-t, --tags <tags...>', 'tags to apply', (values) => {
|
||||
const TagSchema = z.array(
|
||||
z
|
||||
.string()
|
||||
.min(2)
|
||||
.max(20)
|
||||
.regex(/^[a-z0-9-]+$/, 'Tags must contain only lowercase letters, numbers, and hyphens')
|
||||
);
|
||||
|
||||
return validateOrThrow(TagSchema, values, 'tags');
|
||||
})
|
||||
.action((items, options) => {
|
||||
console.log(chalk.green('✓ Items and tags validated'));
|
||||
console.log('Items:', items);
|
||||
if (options.tags) {
|
||||
console.log('Tags:', options.tags);
|
||||
}
|
||||
});
|
||||
|
||||
// Command with custom validation logic
|
||||
program
|
||||
.command('migrate')
|
||||
.description('Database migration with version validation')
|
||||
.option('-f, --from <version>', 'migrate from version', (value) => {
|
||||
const VersionSchema = z.string().regex(/^\d+\.\d+\.\d+$/, 'Version must be in format X.Y.Z');
|
||||
return validateOrThrow(VersionSchema, value, 'from version');
|
||||
})
|
||||
.option('-t, --to <version>', 'migrate to version', (value) => {
|
||||
const VersionSchema = z.string().regex(/^\d+\.\d+\.\d+$/, 'Version must be in format X.Y.Z');
|
||||
return validateOrThrow(VersionSchema, value, 'to version');
|
||||
})
|
||||
.action((options) => {
|
||||
// Additional cross-field validation
|
||||
if (options.from && options.to) {
|
||||
const fromParts = options.from.split('.').map(Number);
|
||||
const toParts = options.to.split('.').map(Number);
|
||||
|
||||
const fromNum = fromParts[0] * 10000 + fromParts[1] * 100 + fromParts[2];
|
||||
const toNum = toParts[0] * 10000 + toParts[1] * 100 + toParts[2];
|
||||
|
||||
if (fromNum >= toNum) {
|
||||
throw new Error('Target version must be higher than source version');
|
||||
}
|
||||
|
||||
console.log(chalk.green('✓ Migration versions validated'));
|
||||
console.log(`Migrating from ${options.from} to ${options.to}`);
|
||||
}
|
||||
});
|
||||
|
||||
// Global error handling
|
||||
program.exitOverride();
|
||||
|
||||
try {
|
||||
program.parse();
|
||||
} catch (error: any) {
|
||||
if (error.code === 'commander.help' || error.code === 'commander.version') {
|
||||
process.exit(0);
|
||||
} else {
|
||||
console.error(chalk.red('Validation Error:'), error.message);
|
||||
console.error(chalk.gray('\nUse --help for usage information'));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
30
skills/commander-patterns/templates/commonjs-commander.js
Normal file
30
skills/commander-patterns/templates/commonjs-commander.js
Normal file
@@ -0,0 +1,30 @@
|
||||
#!/usr/bin/env node
|
||||
const { Command } = require('commander');
|
||||
|
||||
const program = new Command();
|
||||
|
||||
program
|
||||
.name('mycli')
|
||||
.description('A simple CLI tool (CommonJS)')
|
||||
.version('1.0.0');
|
||||
|
||||
program
|
||||
.command('hello')
|
||||
.description('Say hello')
|
||||
.option('-n, --name <name>', 'name to greet', 'World')
|
||||
.action((options) => {
|
||||
console.log(`Hello, ${options.name}!`);
|
||||
});
|
||||
|
||||
program
|
||||
.command('deploy <environment>')
|
||||
.description('Deploy to environment')
|
||||
.option('-f, --force', 'force deployment', false)
|
||||
.action((environment, options) => {
|
||||
console.log(`Deploying to ${environment}...`);
|
||||
if (options.force) {
|
||||
console.log('Force mode enabled');
|
||||
}
|
||||
});
|
||||
|
||||
program.parse();
|
||||
282
skills/commander-patterns/templates/full-cli-example.ts
Normal file
282
skills/commander-patterns/templates/full-cli-example.ts
Normal file
@@ -0,0 +1,282 @@
|
||||
#!/usr/bin/env node
|
||||
import { Command, Option } from 'commander';
|
||||
import chalk from 'chalk';
|
||||
import ora from 'ora';
|
||||
|
||||
const program = new Command();
|
||||
|
||||
// Configure main program
|
||||
program
|
||||
.name('mycli')
|
||||
.description('A powerful CLI tool with all Commander.js features')
|
||||
.version('1.0.0')
|
||||
.option('-c, --config <path>', 'config file path', './config.json')
|
||||
.option('-v, --verbose', 'verbose output', false)
|
||||
.option('--no-color', 'disable colored output');
|
||||
|
||||
// Init command
|
||||
program
|
||||
.command('init')
|
||||
.description('Initialize a new project')
|
||||
.option('-t, --template <type>', 'project template', 'basic')
|
||||
.option('-d, --directory <path>', 'target directory', '.')
|
||||
.option('-f, --force', 'overwrite existing files', false)
|
||||
.action(async (options) => {
|
||||
const spinner = ora('Initializing project...').start();
|
||||
|
||||
try {
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
|
||||
spinner.text = 'Creating directory structure...';
|
||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||
|
||||
spinner.text = 'Copying template files...';
|
||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||
|
||||
spinner.text = 'Installing dependencies...';
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
|
||||
spinner.succeed(chalk.green('Project initialized successfully!'));
|
||||
|
||||
console.log(chalk.blue('\nNext steps:'));
|
||||
console.log(` cd ${options.directory}`);
|
||||
console.log(' mycli dev');
|
||||
} catch (error) {
|
||||
spinner.fail(chalk.red('Initialization failed'));
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
// Dev command
|
||||
program
|
||||
.command('dev')
|
||||
.description('Start development server')
|
||||
.option('-p, --port <port>', 'server port', '3000')
|
||||
.option('-h, --host <host>', 'server host', 'localhost')
|
||||
.option('--open', 'open browser automatically', false)
|
||||
.action((options) => {
|
||||
console.log(chalk.blue('Starting development server...'));
|
||||
console.log(`Server running at http://${options.host}:${options.port}`);
|
||||
|
||||
if (options.open) {
|
||||
console.log(chalk.gray('Opening browser...'));
|
||||
}
|
||||
});
|
||||
|
||||
// Build command
|
||||
program
|
||||
.command('build')
|
||||
.description('Build for production')
|
||||
.addOption(
|
||||
new Option('-m, --mode <mode>', 'build mode')
|
||||
.choices(['development', 'production'])
|
||||
.default('production')
|
||||
)
|
||||
.option('--analyze', 'analyze bundle size', false)
|
||||
.option('--sourcemap', 'generate source maps', false)
|
||||
.action(async (options) => {
|
||||
const spinner = ora('Building for production...').start();
|
||||
|
||||
try {
|
||||
await new Promise((resolve) => setTimeout(resolve, 2000));
|
||||
|
||||
spinner.succeed(chalk.green('Build complete!'));
|
||||
|
||||
console.log(chalk.blue('\nBuild info:'));
|
||||
console.log('Mode:', options.mode);
|
||||
console.log('Source maps:', options.sourcemap ? 'enabled' : 'disabled');
|
||||
|
||||
if (options.analyze) {
|
||||
console.log(chalk.gray('\nBundle analysis:'));
|
||||
console.log(' main.js: 245 KB');
|
||||
console.log(' vendor.js: 892 KB');
|
||||
}
|
||||
} catch (error) {
|
||||
spinner.fail(chalk.red('Build failed'));
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
// Deploy command with nested subcommands
|
||||
const deploy = program.command('deploy').description('Deployment operations');
|
||||
|
||||
deploy
|
||||
.command('start <environment>')
|
||||
.description('Deploy to specified environment')
|
||||
.argument('<environment>', 'target environment (dev, staging, prod)')
|
||||
.addOption(
|
||||
new Option('-s, --strategy <strategy>', 'deployment strategy')
|
||||
.choices(['rolling', 'blue-green', 'canary'])
|
||||
.default('rolling')
|
||||
)
|
||||
.option('-f, --force', 'force deployment', false)
|
||||
.option('--dry-run', 'simulate deployment', false)
|
||||
.action(async (environment, options) => {
|
||||
if (options.dryRun) {
|
||||
console.log(chalk.yellow('🔍 Dry run mode - no actual deployment'));
|
||||
}
|
||||
|
||||
const spinner = ora(`Deploying to ${environment}...`).start();
|
||||
|
||||
try {
|
||||
spinner.text = 'Running tests...';
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
|
||||
spinner.text = 'Building application...';
|
||||
await new Promise((resolve) => setTimeout(resolve, 1500));
|
||||
|
||||
spinner.text = `Deploying with ${options.strategy} strategy...`;
|
||||
await new Promise((resolve) => setTimeout(resolve, 2000));
|
||||
|
||||
spinner.succeed(chalk.green(`Deployed to ${environment}!`));
|
||||
|
||||
console.log(chalk.blue('\nDeployment details:'));
|
||||
console.log('Environment:', environment);
|
||||
console.log('Strategy:', options.strategy);
|
||||
console.log('URL:', `https://${environment}.example.com`);
|
||||
} catch (error) {
|
||||
spinner.fail(chalk.red('Deployment failed'));
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
deploy
|
||||
.command('rollback [version]')
|
||||
.description('Rollback to previous version')
|
||||
.argument('[version]', 'version to rollback to', 'previous')
|
||||
.option('-f, --force', 'skip confirmation', false)
|
||||
.action((version, options) => {
|
||||
if (!options.force) {
|
||||
console.log(chalk.yellow('⚠ This will rollback your deployment. Use --force to confirm.'));
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(chalk.blue(`Rolling back to ${version}...`));
|
||||
console.log(chalk.green('✓ Rollback complete'));
|
||||
});
|
||||
|
||||
deploy
|
||||
.command('status')
|
||||
.description('Check deployment status')
|
||||
.option('-e, --env <environment>', 'check specific environment')
|
||||
.action((options) => {
|
||||
console.log(chalk.blue('Deployment status:'));
|
||||
|
||||
const envs = options.env ? [options.env] : ['dev', 'staging', 'prod'];
|
||||
|
||||
envs.forEach((env) => {
|
||||
console.log(`\n${env}:`);
|
||||
console.log(' Status:', chalk.green('healthy'));
|
||||
console.log(' Version:', '1.2.3');
|
||||
console.log(' Uptime:', '5d 12h 34m');
|
||||
});
|
||||
});
|
||||
|
||||
// Config command group
|
||||
const config = program.command('config').description('Configuration management');
|
||||
|
||||
config
|
||||
.command('get <key>')
|
||||
.description('Get configuration value')
|
||||
.action((key) => {
|
||||
console.log(`${key}: value`);
|
||||
});
|
||||
|
||||
config
|
||||
.command('set <key> <value>')
|
||||
.description('Set configuration value')
|
||||
.action((key, value) => {
|
||||
console.log(chalk.green(`✓ Set ${key} = ${value}`));
|
||||
});
|
||||
|
||||
config
|
||||
.command('list')
|
||||
.description('List all configuration')
|
||||
.option('-f, --format <format>', 'output format (json, yaml, table)', 'table')
|
||||
.action((options) => {
|
||||
console.log(`Configuration (format: ${options.format}):`);
|
||||
console.log('key1: value1');
|
||||
console.log('key2: value2');
|
||||
});
|
||||
|
||||
// Database command group
|
||||
const db = program.command('db').description('Database operations');
|
||||
|
||||
db.command('migrate')
|
||||
.description('Run database migrations')
|
||||
.option('-d, --dry-run', 'show migrations without running')
|
||||
.action((options) => {
|
||||
if (options.dryRun) {
|
||||
console.log(chalk.blue('Migrations to run:'));
|
||||
console.log(' 001_create_users.sql');
|
||||
console.log(' 002_add_email_index.sql');
|
||||
} else {
|
||||
console.log(chalk.blue('Running migrations...'));
|
||||
console.log(chalk.green('✓ 2 migrations applied'));
|
||||
}
|
||||
});
|
||||
|
||||
db.command('seed')
|
||||
.description('Seed database with data')
|
||||
.option('-e, --env <env>', 'environment', 'dev')
|
||||
.action((options) => {
|
||||
console.log(chalk.blue(`Seeding ${options.env} database...`));
|
||||
console.log(chalk.green('✓ Database seeded'));
|
||||
});
|
||||
|
||||
// Test command
|
||||
program
|
||||
.command('test [pattern]')
|
||||
.description('Run tests')
|
||||
.argument('[pattern]', 'test file pattern', '**/*.test.ts')
|
||||
.option('-w, --watch', 'watch mode', false)
|
||||
.option('-c, --coverage', 'collect coverage', false)
|
||||
.option('--verbose', 'verbose output', false)
|
||||
.action((pattern, options) => {
|
||||
console.log(chalk.blue('Running tests...'));
|
||||
console.log('Pattern:', pattern);
|
||||
|
||||
if (options.watch) {
|
||||
console.log(chalk.gray('Watch mode enabled'));
|
||||
}
|
||||
|
||||
if (options.coverage) {
|
||||
console.log(chalk.gray('\nCoverage:'));
|
||||
console.log(' Statements: 85%');
|
||||
console.log(' Branches: 78%');
|
||||
console.log(' Functions: 92%');
|
||||
console.log(' Lines: 87%');
|
||||
}
|
||||
|
||||
console.log(chalk.green('\n✓ 42 tests passed'));
|
||||
});
|
||||
|
||||
// Global error handling
|
||||
program.exitOverride();
|
||||
|
||||
try {
|
||||
program.parse();
|
||||
} catch (error: any) {
|
||||
if (error.code === 'commander.help') {
|
||||
// Help was displayed, exit normally
|
||||
process.exit(0);
|
||||
} else if (error.code === 'commander.version') {
|
||||
// Version was displayed, exit normally
|
||||
process.exit(0);
|
||||
} else {
|
||||
console.error(chalk.red('Error:'), error.message);
|
||||
|
||||
const globalOpts = program.opts();
|
||||
if (globalOpts.verbose) {
|
||||
console.error(chalk.gray('\nStack trace:'));
|
||||
console.error(chalk.gray(error.stack));
|
||||
}
|
||||
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle no command
|
||||
if (process.argv.length <= 2) {
|
||||
program.help();
|
||||
}
|
||||
150
skills/commander-patterns/templates/nested-subcommands.ts
Normal file
150
skills/commander-patterns/templates/nested-subcommands.ts
Normal file
@@ -0,0 +1,150 @@
|
||||
import { Command } from 'commander';
|
||||
import chalk from 'chalk';
|
||||
|
||||
const program = new Command();
|
||||
|
||||
program
|
||||
.name('mycli')
|
||||
.description('CLI with nested subcommands')
|
||||
.version('1.0.0');
|
||||
|
||||
// Config command group
|
||||
const config = program.command('config').description('Manage configuration');
|
||||
|
||||
config
|
||||
.command('get <key>')
|
||||
.description('Get configuration value')
|
||||
.action((key) => {
|
||||
const value = getConfig(key);
|
||||
if (value) {
|
||||
console.log(`${key} = ${value}`);
|
||||
} else {
|
||||
console.log(chalk.yellow(`Config key '${key}' not found`));
|
||||
}
|
||||
});
|
||||
|
||||
config
|
||||
.command('set <key> <value>')
|
||||
.description('Set configuration value')
|
||||
.action((key, value) => {
|
||||
setConfig(key, value);
|
||||
console.log(chalk.green(`✓ Set ${key} = ${value}`));
|
||||
});
|
||||
|
||||
config
|
||||
.command('list')
|
||||
.description('List all configuration')
|
||||
.option('-f, --format <format>', 'output format (json, table)', 'table')
|
||||
.action((options) => {
|
||||
const allConfig = getAllConfig();
|
||||
if (options.format === 'json') {
|
||||
console.log(JSON.stringify(allConfig, null, 2));
|
||||
} else {
|
||||
Object.entries(allConfig).forEach(([key, value]) => {
|
||||
console.log(`${key.padEnd(20)} ${value}`);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
config
|
||||
.command('delete <key>')
|
||||
.description('Delete configuration value')
|
||||
.option('-f, --force', 'skip confirmation', false)
|
||||
.action((key, options) => {
|
||||
if (!options.force) {
|
||||
console.log(chalk.yellow(`Are you sure? Use --force to confirm`));
|
||||
return;
|
||||
}
|
||||
deleteConfig(key);
|
||||
console.log(chalk.green(`✓ Deleted ${key}`));
|
||||
});
|
||||
|
||||
// Database command group
|
||||
const db = program.command('db').description('Database operations');
|
||||
|
||||
db.command('migrate')
|
||||
.description('Run database migrations')
|
||||
.option('-d, --dry-run', 'show what would be migrated')
|
||||
.action((options) => {
|
||||
if (options.dryRun) {
|
||||
console.log(chalk.blue('Dry run: showing migrations...'));
|
||||
} else {
|
||||
console.log(chalk.blue('Running migrations...'));
|
||||
}
|
||||
console.log(chalk.green('✓ Migrations complete'));
|
||||
});
|
||||
|
||||
db.command('seed')
|
||||
.description('Seed database with data')
|
||||
.option('-e, --env <env>', 'environment', 'dev')
|
||||
.action((options) => {
|
||||
console.log(chalk.blue(`Seeding ${options.env} database...`));
|
||||
console.log(chalk.green('✓ Seeding complete'));
|
||||
});
|
||||
|
||||
db.command('reset')
|
||||
.description('Reset database')
|
||||
.option('-f, --force', 'skip confirmation')
|
||||
.action((options) => {
|
||||
if (!options.force) {
|
||||
console.log(chalk.red('⚠ This will delete all data! Use --force to confirm'));
|
||||
return;
|
||||
}
|
||||
console.log(chalk.yellow('Resetting database...'));
|
||||
console.log(chalk.green('✓ Database reset'));
|
||||
});
|
||||
|
||||
// User command group with nested subcommands
|
||||
const user = program.command('user').description('User management');
|
||||
|
||||
const userList = user.command('list').description('List users');
|
||||
userList
|
||||
.option('-p, --page <page>', 'page number', '1')
|
||||
.option('-l, --limit <limit>', 'items per page', '10')
|
||||
.action((options) => {
|
||||
console.log(`Listing users (page ${options.page}, limit ${options.limit})`);
|
||||
});
|
||||
|
||||
user
|
||||
.command('create <username> <email>')
|
||||
.description('Create new user')
|
||||
.option('-r, --role <role>', 'user role', 'user')
|
||||
.action((username, email, options) => {
|
||||
console.log(chalk.green(`✓ Created user ${username} (${email}) with role ${options.role}`));
|
||||
});
|
||||
|
||||
user
|
||||
.command('delete <userId>')
|
||||
.description('Delete user')
|
||||
.option('-f, --force', 'skip confirmation')
|
||||
.action((userId, options) => {
|
||||
if (!options.force) {
|
||||
console.log(chalk.red('Use --force to confirm deletion'));
|
||||
return;
|
||||
}
|
||||
console.log(chalk.green(`✓ Deleted user ${userId}`));
|
||||
});
|
||||
|
||||
// Helper functions (mock implementations)
|
||||
const configStore: Record<string, string> = {
|
||||
apiUrl: 'https://api.example.com',
|
||||
timeout: '5000',
|
||||
};
|
||||
|
||||
function getConfig(key: string): string | undefined {
|
||||
return configStore[key];
|
||||
}
|
||||
|
||||
function setConfig(key: string, value: string): void {
|
||||
configStore[key] = value;
|
||||
}
|
||||
|
||||
function getAllConfig(): Record<string, string> {
|
||||
return { ...configStore };
|
||||
}
|
||||
|
||||
function deleteConfig(key: string): void {
|
||||
delete configStore[key];
|
||||
}
|
||||
|
||||
program.parse();
|
||||
213
skills/commander-patterns/templates/option-class-advanced.ts
Normal file
213
skills/commander-patterns/templates/option-class-advanced.ts
Normal file
@@ -0,0 +1,213 @@
|
||||
import { Command, Option } from 'commander';
|
||||
import chalk from 'chalk';
|
||||
|
||||
const program = new Command();
|
||||
|
||||
program
|
||||
.name('mycli')
|
||||
.description('Advanced Option class usage')
|
||||
.version('1.0.0');
|
||||
|
||||
program
|
||||
.command('deploy')
|
||||
.description('Deploy with advanced option patterns')
|
||||
|
||||
// Option with choices
|
||||
.addOption(
|
||||
new Option('-e, --env <environment>', 'deployment environment')
|
||||
.choices(['dev', 'staging', 'prod'])
|
||||
.default('dev')
|
||||
)
|
||||
|
||||
// Option with custom parser and validation
|
||||
.addOption(
|
||||
new Option('-p, --port <port>', 'server port')
|
||||
.argParser((value) => {
|
||||
const port = parseInt(value, 10);
|
||||
if (isNaN(port) || port < 1 || port > 65535) {
|
||||
throw new Error('Port must be between 1 and 65535');
|
||||
}
|
||||
return port;
|
||||
})
|
||||
.default(3000)
|
||||
)
|
||||
|
||||
// Mandatory option
|
||||
.addOption(
|
||||
new Option('-t, --token <token>', 'API authentication token')
|
||||
.makeOptionMandatory()
|
||||
.env('API_TOKEN')
|
||||
)
|
||||
|
||||
// Option from environment variable
|
||||
.addOption(
|
||||
new Option('-u, --api-url <url>', 'API base URL')
|
||||
.env('API_URL')
|
||||
.default('https://api.example.com')
|
||||
)
|
||||
|
||||
// Conflicting options
|
||||
.addOption(
|
||||
new Option('--use-cache', 'enable caching')
|
||||
.conflicts('noCache')
|
||||
)
|
||||
.addOption(
|
||||
new Option('--no-cache', 'disable caching')
|
||||
.conflicts('useCache')
|
||||
)
|
||||
|
||||
// Implies relationship
|
||||
.addOption(
|
||||
new Option('--ssl', 'enable SSL')
|
||||
.implies({ sslVerify: true })
|
||||
)
|
||||
.addOption(
|
||||
new Option('--ssl-verify', 'verify SSL certificates')
|
||||
)
|
||||
|
||||
// Preset configurations
|
||||
.addOption(
|
||||
new Option('--preset <preset>', 'use preset configuration')
|
||||
.choices(['minimal', 'standard', 'complete'])
|
||||
.argParser((value) => {
|
||||
const presets = {
|
||||
minimal: { replicas: 1, timeout: 30 },
|
||||
standard: { replicas: 3, timeout: 60 },
|
||||
complete: { replicas: 5, timeout: 120 },
|
||||
};
|
||||
return presets[value as keyof typeof presets];
|
||||
})
|
||||
)
|
||||
|
||||
// Hidden option (for debugging)
|
||||
.addOption(
|
||||
new Option('--debug', 'enable debug mode')
|
||||
.hideHelp()
|
||||
)
|
||||
|
||||
// Custom option processing
|
||||
.addOption(
|
||||
new Option('-r, --replicas <count>', 'number of replicas')
|
||||
.argParser((value, previous) => {
|
||||
const count = parseInt(value, 10);
|
||||
if (count < 1) {
|
||||
throw new Error('Replicas must be at least 1');
|
||||
}
|
||||
return count;
|
||||
})
|
||||
.default(3)
|
||||
)
|
||||
|
||||
.action((options) => {
|
||||
console.log(chalk.blue('Deployment configuration:'));
|
||||
console.log('Environment:', chalk.yellow(options.env));
|
||||
console.log('Port:', options.port);
|
||||
console.log('Token:', options.token ? chalk.green('***set***') : chalk.red('not set'));
|
||||
console.log('API URL:', options.apiUrl);
|
||||
console.log('Cache:', options.useCache ? 'enabled' : 'disabled');
|
||||
console.log('SSL:', options.ssl ? 'enabled' : 'disabled');
|
||||
console.log('SSL Verify:', options.sslVerify ? 'enabled' : 'disabled');
|
||||
|
||||
if (options.preset) {
|
||||
console.log('Preset configuration:', options.preset);
|
||||
}
|
||||
|
||||
console.log('Replicas:', options.replicas);
|
||||
|
||||
if (options.debug) {
|
||||
console.log(chalk.gray('\nDebug mode enabled'));
|
||||
console.log(chalk.gray('All options:'), options);
|
||||
}
|
||||
|
||||
console.log(chalk.green('\n✓ Deployment configuration validated'));
|
||||
});
|
||||
|
||||
// Command demonstrating option dependencies
|
||||
program
|
||||
.command('backup')
|
||||
.description('Backup data with option dependencies')
|
||||
.addOption(
|
||||
new Option('-m, --method <method>', 'backup method')
|
||||
.choices(['full', 'incremental', 'differential'])
|
||||
.makeOptionMandatory()
|
||||
)
|
||||
.addOption(
|
||||
new Option('--base-backup <path>', 'base backup for incremental')
|
||||
.conflicts('full')
|
||||
)
|
||||
.addOption(
|
||||
new Option('--compression <level>', 'compression level')
|
||||
.choices(['none', 'low', 'medium', 'high'])
|
||||
.default('medium')
|
||||
)
|
||||
.action((options) => {
|
||||
if (options.method === 'incremental' && !options.baseBackup) {
|
||||
console.log(chalk.red('Error: --base-backup required for incremental backup'));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log(chalk.blue(`Running ${options.method} backup...`));
|
||||
console.log('Compression:', options.compression);
|
||||
if (options.baseBackup) {
|
||||
console.log('Base backup:', options.baseBackup);
|
||||
}
|
||||
console.log(chalk.green('✓ Backup complete'));
|
||||
});
|
||||
|
||||
// Command with complex validation
|
||||
program
|
||||
.command('scale')
|
||||
.description('Scale application with complex validation')
|
||||
.addOption(
|
||||
new Option('-r, --replicas <count>', 'number of replicas')
|
||||
.argParser((value) => {
|
||||
const count = parseInt(value, 10);
|
||||
if (isNaN(count)) {
|
||||
throw new Error('Replicas must be a number');
|
||||
}
|
||||
if (count < 1 || count > 100) {
|
||||
throw new Error('Replicas must be between 1 and 100');
|
||||
}
|
||||
return count;
|
||||
})
|
||||
.makeOptionMandatory()
|
||||
)
|
||||
.addOption(
|
||||
new Option('--cpu <value>', 'CPU limit (millicores)')
|
||||
.argParser((value) => {
|
||||
const cpu = parseInt(value, 10);
|
||||
if (cpu < 100 || cpu > 8000) {
|
||||
throw new Error('CPU must be between 100 and 8000 millicores');
|
||||
}
|
||||
return cpu;
|
||||
})
|
||||
.default(1000)
|
||||
)
|
||||
.addOption(
|
||||
new Option('--memory <value>', 'Memory limit (MB)')
|
||||
.argParser((value) => {
|
||||
const mem = parseInt(value, 10);
|
||||
if (mem < 128 || mem > 16384) {
|
||||
throw new Error('Memory must be between 128 and 16384 MB');
|
||||
}
|
||||
return mem;
|
||||
})
|
||||
.default(512)
|
||||
)
|
||||
.action((options) => {
|
||||
console.log(chalk.blue('Scaling application:'));
|
||||
console.log('Replicas:', chalk.yellow(options.replicas));
|
||||
console.log('CPU:', `${options.cpu}m`);
|
||||
console.log('Memory:', `${options.memory}MB`);
|
||||
|
||||
const totalCpu = options.replicas * options.cpu;
|
||||
const totalMemory = options.replicas * options.memory;
|
||||
|
||||
console.log(chalk.gray('\nTotal resources:'));
|
||||
console.log(chalk.gray(`CPU: ${totalCpu}m`));
|
||||
console.log(chalk.gray(`Memory: ${totalMemory}MB`));
|
||||
|
||||
console.log(chalk.green('\n✓ Scaling complete'));
|
||||
});
|
||||
|
||||
program.parse();
|
||||
46
skills/commander-patterns/templates/package.json.template
Normal file
46
skills/commander-patterns/templates/package.json.template
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"name": "{{CLI_NAME}}",
|
||||
"version": "1.0.0",
|
||||
"description": "{{DESCRIPTION}}",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"bin": {
|
||||
"{{CLI_NAME}}": "dist/index.js"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"dev": "tsx watch src/index.ts",
|
||||
"start": "node dist/index.js",
|
||||
"lint": "eslint src --ext .ts",
|
||||
"format": "prettier --write 'src/**/*.ts'",
|
||||
"test": "vitest",
|
||||
"prepublishOnly": "npm run build"
|
||||
},
|
||||
"keywords": [
|
||||
"cli",
|
||||
"commander",
|
||||
"typescript"
|
||||
],
|
||||
"author": "{{AUTHOR}}",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"commander": "^12.0.0",
|
||||
"chalk": "^5.3.0",
|
||||
"ora": "^8.0.1",
|
||||
"inquirer": "^9.2.15"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.11.24",
|
||||
"@types/inquirer": "^9.0.7",
|
||||
"typescript": "^5.3.3",
|
||||
"tsx": "^4.7.1",
|
||||
"eslint": "^8.57.0",
|
||||
"@typescript-eslint/eslint-plugin": "^7.1.0",
|
||||
"@typescript-eslint/parser": "^7.1.0",
|
||||
"prettier": "^3.2.5",
|
||||
"vitest": "^1.3.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
}
|
||||
26
skills/commander-patterns/templates/tsconfig.commander.json
Normal file
26
skills/commander-patterns/templates/tsconfig.commander.json
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"module": "ESNext",
|
||||
"lib": ["ES2022"],
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"allowJs": true,
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
"removeComments": true,
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"sourceMap": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
Reference in New Issue
Block a user