eddiebelaval/squire/cli-builder

Claude Code and agent skill by eddiebelaval/squire

Trust tier 3
  • community
  • ext:[{"type":"config","description":"Configuration overrides"}]
Source check pending

Checking that the SKILL.md file is still in the repository.

Install

npx vskill@latest install eddiebelaval/squire/cli-builder

Semi-extensibleMentions customization, but needs skill-specific knowledge. Read the extensible skills standard

SKILL.md


name: cli-builder description: Expert guide for building command-line interfaces with Node.js (Commander, Inquirer, Ora) or Python (Click, Typer, Rich). Use when creating CLI tools, terminal UX, argument parsing, or interactive prompts. slug: cli-builder category: operations complexity: complex version: "1.0.0" author: "id8Labs" triggers:

  • "cli-builder"
  • "cli builder"

tags:

  • development
  • tool-factory-retrofitted---

CLI Builder Skill

Core Workflows

Workflow 1: Primary Action
  1. Analyze the input and context
  2. Validate prerequisites are met
  3. Execute the core operation
  4. Verify the output meets expectations
  5. Report results

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
  1. Predictable: Follow conventions users expect
  2. Helpful: Provide clear help text and error messages
  3. Composable: Work well with pipes and other tools
  4. 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
bash
# 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
json
{
"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
typescript
// 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
typescript
// 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
typescript
// 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
typescript
// 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<ProjectAnswers> {
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
typescript
#!/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 <template>', 'Template to use')
.option('-y, --yes', 'Skip prompts with defaults')
.action(async (name, options) => {
try {
// Get project name if not provided
if (!name) {
const { projectName } = await inquirer.prompt([{
type: 'input',
name: 'projectName',
message: 'Project name:',
default: 'my-app'
}]);
name = projectName;
}
// Check if directory exists
const projectDir = join(process.cwd(), name);
if (existsSync(projectDir)) {
const { overwrite } = await inquirer.prompt([{
type: 'confirm',
name: 'overwrite',
message: `Directory ${name} exists. Overwrite?`,
default: false
}]);
if (!overwrite) {
console.log(chalk.yellow('Aborted.'));
process.exit(0);
}
}
// Get template if not provided
let template = options.template;
if (!template && !options.yes) {
const { selectedTemplate } = await inquirer.prompt([{
type: 'list',
name: 'selectedTemplate',
message: 'Select template:',
choices: ['minimal', 'standard', 'typescript']
}]);
template = selectedTemplate;
}
template = template || 'standard';
console.log();
console.log(chalk.bold(`Creating ${name} with ${template} template...`));
console.log();
// Create project
const spinner = ora('Creating directory structure').start();
mkdirSync(projectDir, { recursive: true });
spinner.succeed();
spinner.start('Generating files');
writeFileSync(
join(projectDir, 'package.json'),
JSON.stringify({ name, version: '1.0.0' }, null, 2)
);
spinner.succeed();
// Success message
console.log();
console.log(chalk.green.bold('Success!'), `Created ${name}`);
console.log();
console.log('Next steps:');
console.log(chalk.cyan(` cd ${name}`));
console.log(chalk.cyan(' npm install'));
console.log(chalk.cyan(' npm start'));
console.log();
} catch (error) {
console.error(chalk.red('Error:'), error.message);
process.exit(1);
}
});
// Handle unknown commands
program.on('command:*', () => {
console.error(chalk.red('Unknown command:'), program.args.join(' '));
console.log('Run', chalk.cyan('create-app --help'), 'for usage');
process.exit(1);
});
// Parse and handle no command
program.parse();
if (!process.argv.slice(2).length) {
program.help();
}

Python CLI Development

Typer - Modern Python CLI
python
# cli.py
import typer
from typing import Optional, List
from enum import Enum
from rich.console import Console
from rich.table import Table
from rich.progress import track
app = typer.Typer(
name="mycli",
help="A powerful CLI for doing awesome things",
add_completion=True
)
console = Console()
class Template(str, Enum):
minimal = "minimal"
standard = "standard"
full = "full"

This is the start of the file. Read the full SKILL.md on GitHub.

Security

  • Security scanPassed, 95 of 100Pattern checks on the skill's files.
  • Model reviewPassed

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

7-day trend score
5

Not refreshed yet.

Extension points

  • configConfiguration overrides

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/eddiebelaval/squire/cli-builder/badge)](https://verified-skill.com/skills/eddiebelaval/squire/cli-builder)

Something wrong with this skill or its page? Tell us.