CLI Design Expert
Overview
This skill provides expert guidance for designing and implementing professional CLI tools with:
- Intuitive UX: Commands that work as users expect
- Progressive Disclosure: Simple by default, powerful when needed
- Excellent Help: Self-documenting commands with rich examples
- Error Recovery: Helpful errors that guide users to success
- Professional Polish: Consistent styling, colors, and output formatting
PROACTIVE USAGE
Invoke this skill before:
- Creating new CLI commands
- Designing command structures
- Writing help text and documentation
- Implementing error messages
- Adding interactive prompts
Critical Design Principles
1. Command Structure - Follow Git's Model
# Noun-verb pattern (preferred)
uam memory query # <tool> <resource> <action>
uam worktree create # <tool> <resource> <action>
# Common structure
<tool> <command> [subcommand] [arguments] [--options]
# Examples
uam init # Simple command
uam init --interactive # With flag
uam generate --output ./out # With option value
uam memory query "search term" # With argument
uam worktree create fix-bug --base develop # Full example
2. Option Naming Conventions
# Short + Long options (always provide both for common options)
-v, --version # Version
-h, --help # Help
-o, --output <path> # Output path
-f, --force # Force/overwrite
-q, --quiet # Suppress output
-d, --debug # Debug mode
-n, --dry-run # Preview without changes
# Flags (boolean) vs Options (values)
--verbose # Flag (boolean)
--format json # Option with value
--count 10 # Option with number
# Negatable flags
--color / --no-color # Allow disabling defaults
--cache / --no-cache
3. Exit Codes
// Standard exit codes
const EXIT_SUCCESS = 0; // Success
const EXIT_ERROR = 1; // General error
const EXIT_USAGE = 2; // Invalid usage/arguments
const EXIT_CONFIG = 78; // Configuration error
const EXIT_NOINPUT = 66; // Input file not found
const EXIT_CANTCREAT = 73; // Can't create output
// Usage
process.exit(EXIT_SUCCESS);
process.exit(EXIT_ERROR);
Help System Design
1. Three Levels of Help
# Level 1: Command overview (--help on root)
$ uam --help
Universal Agent Memory - AI agent memory and workflow system
Usage: uam [command] [options]
Commands:
init Initialize a new project
generate Generate CLAUDE.md and agent files
memory Manage agent memory (short-term and long-term)
worktree Git worktree management for isolated development
Options:
-v, --version Show version number
-h, --help Show help
Run 'uam <command> --help' for more information on a command.
# Level 2: Command help (--help on command)
$ uam memory --help
Manage agent memory systems
Usage: uam memory <subcommand> [options]
Subcommands:
query Search long-term memory
store Store a new memory
status Show memory system status
start Start memory services
Examples:
uam memory query "redis caching"
uam memory store lesson "Always check network policies" --tags networking --importance 8
# Level 3: Subcommand help (detailed with examples)
$ uam memory query --help
Search long-term memory using semantic similarity
Usage: uam memory query <search-term> [options]
Arguments:
search-term Keywords to search for
Options:
-l, --limit <n> Maximum results (default: 10)
-t, --tags <tags> Filter by tags (comma-separated)
--min-score <n> Minimum similarity score (0-1, default: 0.5)
--json Output as JSON
Examples:
# Basic search
uam memory query "authentication flow"
# Search with filters
uam memory query "database" --tags postgres,migration --limit 5
# JSON output for scripting
uam memory query "API design" --json | jq '.results[0]'
2. Example-Driven Documentation
// Every command should have at least 3 examples
const command = new Command('generate')
.description('Generate CLAUDE.md and agent configuration files')
.option('-o, --output <path>', 'Output directory', '.')
.option('--dry-run', 'Preview without writing files')
.addHelpText('after', `
Examples:
# Generate with defaults
$ uam generate
# Generate to specific directory
$ uam generate --output ./docs
# Preview what would be generated
$ uam generate --dry-run
# Generate for specific platform
$ uam generate --platform factory
Common Issues:
If generation fails, ensure you have a .uam.json config file.
Run 'uam init' to create one interactively.
`);
Error Message Design
1. Helpful Error Format
// ❌ BAD - Cryptic error
throw new Error('ENOENT');
// ✅ GOOD - Helpful error with solution
console.error(`
${chalk.red('Error:')} Configuration file not found
Looking for: ${chalk.cyan('.uam.json')}
Searched in: ${chalk.dim(process.cwd())}
${chalk.yellow('How to fix:')}
Run ${chalk.cyan('uam init')} to create a configuration file.
${chalk.dim('For more help: uam init --help')}
`);
2. Error Categories
interface CLIError {
code: string;
message: string;
suggestion?: string;
docs?: string;
}
const ERROR_MESSAGES: Record<string, CLIError> = {
CONFIG_NOT_FOUND: {
code: 'CONFIG_NOT_FOUND',
message: 'Configuration file .uam.json not found',
suggestion: 'Run `uam init` to create a configuration file',
docs: 'https://github.com/DammianMiller/universal-agent-memory#configuration',
},
INVALID_CONFIG: {
code: 'INVALID_CONFIG',
message: 'Configuration file is invalid',
suggestion: 'Check the JSON syntax and required fields',
docs: 'https://github.com/DammianMiller/universal-agent-memory#configuration',
},
GIT_NOT_FOUND: {
code: 'GIT_NOT_FOUND',
message: 'Not a git repository',
suggestion: 'Initialize git with `git init` or run from a git repository',
},
};
function formatError(error: CLIError): void {
console.error(chalk.red(`\nError [${error.code}]:`), error.message);
if (error.suggestion) {
console.error(chalk.yellow('\nSuggestion:'), error.suggestion);
}
if (error.docs) {
console.error(chalk.dim('\nDocumentation:'), error.docs);
}
}
3. Validation Errors
// Show all validation errors at once
function validateConfig(config: unknown): ValidationResult {
const errors: string[] = [];
if (!config || typeof config !== 'object') {
return { valid: false, errors: ['Configuration must be an object'] };
}
const c = config as Record<string, unknown>;
if (!c.project) {
errors.push('Missing required field: project');
}
if (!c.project?.name) {
errors.push('Missing required field: project.name');
}
if (c.memory?.shortTerm?.maxEntries && typeof c.memory.shortTerm.maxEntries !== 'number') {
errors.push('Invalid type: memory.shortTerm.maxEntries must be a number');
}
return { valid: errors.length === 0, errors };
}
// Display validation errors nicely
function showValidationErrors(errors: string[]): void {
console.error(chalk.red('\nConfiguration validation failed:\n'));
errors.forEach((err, i) => {
console.error(chalk.red(` ${i + 1}.`), err);
});
console.error(chalk.dim('\nCheck .uam.json and fix the issues above.'));
}
Interactive Prompts
1. Inquirer.js Patterns
import inquirer from 'inquirer';
// Grouped questions with conditional flow
async function initInteractive(): Promise<Config> {
const answers = await inquirer.prompt([
{
type: 'input',
name: 'projectName',
message: 'Project name:',
default: basename(process.cwd()),
validate: (input) => input.length > 0 || 'Project name is required',
},
{
ty