Claude Code Plugins

Community-maintained marketplace

Feedback

Expert CLI/TUI designer for building intuitive, user-friendly, and professional command-line interfaces. Focuses on UX patterns, help systems, progressive disclosure, and developer ergonomics.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name cli-design-expert
description Expert CLI/TUI designer for building intuitive, user-friendly, and professional command-line interfaces. Focuses on UX patterns, help systems, progressive disclosure, and developer ergonomics.

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',
    },
    {
      type: 'input',
      name: 'description',
      message: 'Description (optional):',
    },
    {
      type: 'list',
      name: 'platform',
      message: 'Primary AI platform:',
      choices: [
        { name: 'Claude Code (Desktop)', value: 'claudeCode' },
        { name: 'Factory.AI', value: 'factory' },
        { name: 'VS Code', value: 'vscode' },
        { name: 'OpenCode', value: 'opencode' },
      ],
    },
    {
      type: 'confirm',
      name: 'enableMemory',
      message: 'Enable memory system?',
      default: true,
    },
    {
      type: 'list',
      name: 'memoryBackend',
      message: 'Long-term memory backend:',
      choices: [
        { name: 'Qdrant (local Docker)', value: 'qdrant' },
        { name: 'Qdrant Cloud', value: 'qdrant-cloud' },
        { name: 'GitHub (JSON files)', value: 'github' },
        { name: 'None', value: 'none' },
      ],
      when: (answers) => answers.enableMemory,
    },
  ]);

  return buildConfig(answers);
}

2. Confirmation for Destructive Actions

async function handleDestructiveAction(
  action: string,
  details: string,
  execute: () => Promise<void>
): Promise<void> {
  console.log(chalk.yellow(`\n⚠️  ${action}\n`));
  console.log(chalk.dim(details));
  
  const { confirmed } = await inquirer.prompt([{
    type: 'confirm',
    name: 'confirmed',
    message: 'Are you sure you want to proceed?',
    default: false,
  }]);

  if (!confirmed) {
    console.log(chalk.dim('Cancelled.'));
    return;
  }

  await execute();
}

// Usage
await handleDestructiveAction(
  'Delete worktree and branch',
  `This will delete:\n  - Worktree: .worktrees/123-feature\n  - Branch: feature/123-feature`,
  async () => await deleteWorktree(id)
);

Output Formatting

1. Tables

// Simple aligned table
function printTable(headers: string[], rows: string[][]): void {
  const widths = headers.map((h, i) => 
    Math.max(h.length, ...rows.map(r => (r[i] || '').length))
  );
  
  // Header
  console.log(headers.map((h, i) => h.padEnd(widths[i]!)).join('  '));
  console.log(widths.map(w => '─'.repeat(w)).join('  '));
  
  // Rows
  for (const row of rows) {
    console.log(row.map((cell, i) => (cell || '').padEnd(widths[i]!)).join('  '));
  }
}

// Usage
printTable(
  ['ID', 'Branch', 'Status', 'Created'],
  [
    ['1', 'feature/add-auth', 'active', '2024-01-15'],
    ['2', 'fix/memory-leak', 'merged', '2024-01-14'],
  ]
);

2. JSON Output for Scripting

interface CommandOptions {
  json?: boolean;
  quiet?: boolean;
}

function output<T>(data: T, options: CommandOptions): void {
  if (options.json) {
    console.log(JSON.stringify(data, null, 2));
    return;
  }
  
  if (options.quiet) {
    // Minimal output for scripting
    if (Array.isArray(data)) {
      data.forEach(item => console.log(item.id || item));
    } else {
      console.log((data as { id?: string }).id || data);
    }
    return;
  }
  
  // Human-readable output
  prettyPrint(data);
}

3. Progress Indicators

import ora from 'ora';

// Single task
const spinner = ora('Processing...').start();
try {
  await doWork();
  spinner.succeed('Done!');
} catch (e) {
  spinner.fail('Failed');
  throw e;
}

// Multi-step with status
async function runPipeline(steps: Array<{ name: string; run: () => Promise<void> }>): Promise<void> {
  for (let i = 0; i < steps.length; i++) {
    const step = steps[i]!;
    const prefix = chalk.dim(`[${i + 1}/${steps.length}]`);
    const spinner = ora(`${prefix} ${step.name}`).start();
    
    try {
      await step.run();
      spinner.succeed(`${prefix} ${step.name}`);
    } catch (e) {
      spinner.fail(`${prefix} ${step.name}`);
      throw e;
    }
  }
}

Color Usage

import chalk from 'chalk';

// Semantic colors
const colors = {
  // Status
  success: chalk.green,      // ✔ Operations completed
  error: chalk.red,          // ✖ Errors
  warning: chalk.yellow,     // ⚠ Warnings
  info: chalk.cyan,          // ℹ Information
  
  // Emphasis
  primary: chalk.blue,       // Important values
  secondary: chalk.dim,      // Less important
  highlight: chalk.bold,     // Emphasis
  
  // Data types
  path: chalk.cyan,          // File paths
  command: chalk.cyan,       // Commands to run
  code: chalk.yellow,        // Code snippets
  url: chalk.underline.blue, // URLs
};

// Symbols
const symbols = {
  success: chalk.green('✔'),
  error: chalk.red('✖'),
  warning: chalk.yellow('⚠'),
  info: chalk.cyan('ℹ'),
  bullet: chalk.dim('•'),
  arrow: chalk.dim('→'),
};

Shell Completion

// Generate completion script
program
  .command('completion')
  .description('Generate shell completion script')
  .argument('<shell>', 'Shell type (bash, zsh, fish)')
  .action((shell: string) => {
    switch (shell) {
      case 'bash':
        console.log(generateBashCompletion());
        break;
      case 'zsh':
        console.log(generateZshCompletion());
        break;
      case 'fish':
        console.log(generateFishCompletion());
        break;
      default:
        console.error(`Unknown shell: ${shell}`);
        process.exit(1);
    }
  });

// Example bash completion
function generateBashCompletion(): string {
  return `
_uam_completions() {
  local cur="\${COMP_WORDS[COMP_CWORD]}"
  local prev="\${COMP_WORDS[COMP_CWORD-1]}"
  
  case "\${prev}" in
    uam)
      COMPREPLY=($(compgen -W "init generate memory worktree droids" -- "\${cur}"))
      ;;
    memory)
      COMPREPLY=($(compgen -W "query store status start stop" -- "\${cur}"))
      ;;
    worktree)
      COMPREPLY=($(compgen -W "create list pr cleanup" -- "\${cur}"))
      ;;
  esac
}

complete -F _uam_completions uam
`;
}

Review Checklist

Before releasing any CLI command:

  • --help provides clear, example-rich documentation
  • Short and long options for common flags
  • Exit codes are meaningful (0 = success, non-zero = error)
  • Error messages explain what went wrong AND how to fix it
  • Destructive actions require confirmation (unless --force)
  • --json output available for scripting
  • --quiet / --verbose options where appropriate
  • Colors are used semantically and respect NO_COLOR env var
  • Progress indicators for long-running operations
  • Validation shows all errors at once, not one at a time