npx skills add https://github.com/eddiebe147/claude-settings --skill cli-builderSKILL.md
CLI Builder Skill
Overview
This skill helps you build professional command-line interfaces with excellent user experience. Covers argument parsing, interactive prompts, progress indicators, colored output, and cross-platform compatibility.
CLI Design Philosophy
Principles of Good CLI Design
- Predictable: Follow conventions users expect
- Helpful: Provide clear help text and error messages
- Composable: Work well with pipes and other tools
- Forgiving: Accept common variations in input
Design Guidelines
- DO: Use conventional flag names (
-v,--verbose,-h,--help) - DO: Provide meaningful exit codes
- DO: Support
--versionand--helpon all commands - DO: Use colors meaningfully (errors=red, success=green)
- DON'T: Require interactive input when running in pipes
- DON'T: Print to stdout when outputting errors
- DON'T: Ignore signals (Ctrl+C should exit cleanly)
Node.js CLI Development
Project Setup
# Initialize CLI project
mkdir my-cli && cd my-cli
npm init -y
# Install core dependencies
npm install commander chalk ora inquirer
# Optional: TypeScript support
npm install -D typescript @types/node @types/inquirer ts-node
Package.json Configuration
{
"name": "my-cli",
"version": "1.0.0",
"description": "A powerful CLI tool",
"bin": {
"mycli": "./bin/cli.js"
},
"files": [
"bin",
"dist"
],
"scripts": {
"build": "tsc",
"dev": "ts-node src/cli.ts",
"link": "npm link"
},
"engines": {
"node": ">=18.0.0"
}
}
Commander.js - Command Structure
// src/cli.ts
import { Command } from 'commander';
import { version } from '../package.json';
const program = new Command();
program
.name('mycli')
.description('A powerful CLI for doing awesome things')
.version(version, '-v, --version', 'Display version number');
// Simple command
program
.command('init')
.description('Initialize a new project')
.argument('[name]', 'Project name', 'my-project')
.option('-t, --template <type>', 'Template to use', 'default')
.option('--no-git', 'Skip git initialization')
.option('-f, --force', 'Overwrite existing files')
.action(async (name, options) => {
console.log(`Creating project: ${name}`);
console.log(`Template: ${options.template}`);
console.log(`Git: ${options.git}`);
});
// Command with subcommands
const config = program
.command('config')
.description('Manage configuration');
config
.command('get <key>')
.description('Get a configuration value')
.action((key) => {
console.log(`Getting config: ${key}`);
});
config
.command('set <key> <value>')
.description('Set a configuration value')
.action((key, value) => {
console.log(`Setting ${key} = ${value}`);
});
config
.command('list')
.description('List all configuration')
.option('--json', 'Output as JSON')
.action((options) => {
if (options.json) {
console.log(JSON.stringify({ key: 'value' }, null, 2));
} else {
console.log('key = value');
}
});
// Parse arguments
program.parse();
Chalk - Colored Output
// src/utils/logger.ts
import chalk from 'chalk';
export const logger = {
info: (msg: string) => console.log(chalk.blue('info'), msg),
success: (msg: string) => console.log(chalk.green('success'), msg),
warning: (msg: string) => console.log(chalk.yellow('warning'), msg),
error: (msg: string) => console.error(chalk.red('error'), msg),
// Styled output
title: (msg: string) => console.log(chalk.bold.underline(msg)),
dim: (msg: string) => console.log(chalk.dim(msg)),
// Formatted output
list: (items: string[]) => {
items.forEach(item => console.log(chalk.gray(' -'), item));
},
// Table-like output
keyValue: (pairs: Record<string, string>) => {
const maxKeyLen = Math.max(...Object.keys(pairs).map(k => k.length));
Object.entries(pairs).forEach(([key, value]) => {
console.log(
chalk.cyan(key.padEnd(maxKeyLen)),
chalk.gray(':'),
value
);
});
}
};
// Usage
logger.title('Project Configuration');
logger.keyValue({
'Name': 'my-project',
'Template': 'typescript',
'Version': '1.0.0'
});
Ora - Progress Spinners
// src/utils/spinner.ts
import ora, { Ora } from 'ora';
export function createSpinner(text: string): Ora {
return ora({
text,
spinner: 'dots',
color: 'cyan'
});
}
// Usage patterns
async function downloadWithProgress() {
const spinner = createSpinner('Downloading dependencies...');
spinner.start();
try {
await downloadFiles();
spinner.succeed('Dependencies downloaded');
} catch (error) {
spinner.fail('Download failed');
throw error;
}
}
// Sequential spinners
async function setupProject() {
const steps = [
{ text: 'Creating directory structure', fn: createDirs },
{ text: 'Installing dependencies', fn: installDeps },
...
Repository Stats
Stars6
Forks1