cli-builder

安装量: 58
排名: #12782

安装

npx skills add https://github.com/eddiebe147/claude-settings --skill cli-builder

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 --version and --help on 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 ', '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 ') .description('Get a configuration value') .action((key) => { console.log(Getting config: ${key}); });

config .command('set ') .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) => { 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 }, { text: 'Initializing git', fn: initGit }, { text: 'Configuring project', fn: configure } ];

for (const step of steps) { const spinner = createSpinner(step.text); spinner.start(); try { await step.fn(); spinner.succeed(); } catch (error) { spinner.fail(); throw error; } } }

Inquirer - Interactive Prompts // src/prompts/init.ts import inquirer from 'inquirer';

interface ProjectAnswers { name: string; template: string; features: string[]; initGit: boolean; installDeps: boolean; }

export async function promptProjectSetup(): Promise { return inquirer.prompt([ { type: 'input', name: 'name', message: 'Project name:', default: 'my-project', validate: (input) => { if (!/^[a-z0-9-]+$/.test(input)) { return 'Name must be lowercase alphanumeric with dashes'; } return true; } }, { type: 'list', name: 'template', message: 'Select a template:', choices: [ { name: 'Minimal - Basic setup', value: 'minimal' }, { name: 'Standard - Recommended defaults', value: 'standard' }, { name: 'Full - Kitchen sink', value: 'full' } ], default: 'standard' }, { 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 (GitHub Actions)', value: 'github-actions' } ] }, { type: 'confirm', name: 'initGit', message: 'Initialize git repository?', default: true }, { type: 'confirm', name: 'installDeps', message: 'Install dependencies now?', default: true, when: (answers) => answers.template !== 'minimal' } ]); }

// Advanced: Dynamic prompts export async function promptWithContext(context: { hasExisting: boolean }) { const questions = [];

if (context.hasExisting) { questions.push({ type: 'confirm', name: 'overwrite', message: 'Directory exists. Overwrite?', default: false }); }

// Add more questions...

return inquirer.prompt(questions); }

Complete CLI Example

!/usr/bin/env node

// bin/cli.ts

import { Command } from 'commander'; import chalk from 'chalk'; import ora from 'ora'; import inquirer from 'inquirer'; import { existsSync, mkdirSync, writeFileSync } from 'fs'; import { join } from 'path';

const program = new Command();

program .name('create-app') .description('Create a new application') .version('1.0.0');

program .command('create') .argument('[name]', 'Project name') .option('-t, --template