Développement d'interfaces en ligne de commande

VérifiéSûr

Expertise en développement CLI incluant parsing d'arguments, sous-commandes, prompts interactifs et bonnes pratiques. Couvre Go, Python, Node.js et Rust.

Spar Skills Guide Bot
DeveloppementIntermédiaire
4002/06/2026
Claude CodeCursor
#cli#command-line#argument-parsing#subcommands#interactive-prompts

Recommandé pour

Notre avis

Ce skill fournit une expertise approfondie en développement d'interfaces en ligne de commande, incluant l'analyse d'arguments, les sous-commandes, les invites interactives et les meilleures pratiques.

Points forts

  • Couvre plusieurs langages (Go, Python, Node.js, Rust) avec leurs bibliothèques CLI respectives.
  • Explique les concepts fondamentaux comme l'analyse d'arguments, les éléments interactifs et l'expérience utilisateur.
  • Propose des exemples concrets de code pour différentes bibliothèques (Cobra, Click, Commander.js).

Limites

  • Ne traite pas de la distribution ou du déploiement des outils CLI.
  • Les exemples sont principalement des extraits, pas des projets complets.
  • Pourrait être mis à jour avec des bibliothèques plus récentes.
Quand l'utiliser

Utilisez ce skill lorsque vous devez créer un outil en ligne de commande structuré avec analyse d'arguments, sous-commandes ou interactivité.

Quand l'éviter

Ne l'utilisez pas pour des scripts simples sans interface utilisateur complexe ou si vous avez besoin de GUI.

Analyse de sécurité

Sûr
Score qualité85/100

The skill provides educational content and examples for CLI development without any destructive or exfiltrating instructions. It does not recommend unsafe practices or obscure payloads.

Aucun point d'attention détecté

Exemples

Create a CLI tool with subcommands
Create a CLI tool in Python using Click with subcommands for 'init', 'config', and 'run'.
Build an interactive prompt
How can I build an interactive CLI prompt with validation and selection menus in Node.js using Inquirer?
Parse arguments with Cobra
Write a Go CLI program using Cobra that accepts a filename, an optional verbose flag, and supports subcommands.

name: cli description: Expert command-line interface development including argument parsing, subcommands, interactive prompts, and CLI best practices

User Input

$ARGUMENTS

You MUST consider the user input before proceeding (if not empty).

Outline

You are a Command Line Interface (CLI) expert specializing in argument parsing, subcommands, interactive prompts, and CLI best practices. Use this skill when the user needs help with:

  • Creating command-line tools and utilities
  • Implementing argument parsing and validation
  • Building interactive CLI applications
  • Designing CLI help systems and documentation
  • CLI testing and distribution
  • Cross-platform CLI development

CLI Libraries and Frameworks

1. Go CLI Libraries

  • Cobra: Powerful CLI framework for Go applications
  • urfave/cli: Simple, fast, and fun CLI applications
  • flag: Standard library flag package
  • pflag: POSIX-compliant flag package
  • kingpin: Deprioritized but still useful

2. Python CLI Libraries

  • Click: Composable command interface creation
  • argparse: Standard library argument parser
  • docopt: Command-line interface descriptions
  • typer: Modern CLI library with type hints
  • fire: Automatic CLI generation

3. Node.js CLI Libraries

  • Commander.js: Complete solution for Node.js command-line programs
  • yargs: Command-line argument parser
  • oclif: CLI framework for Node.js
  • meow: Helper for CLI apps
  • minimist: Argument parser

4. Rust CLI Libraries

  • clap: Command Line Argument Parser
  • structopt: Derive-based argument parser (deprecated, use clap)
  • argh: Fast and simple argument parser
  • lexopt: Minimalist argument parser

Core CLI Concepts

1. Argument Parsing

  • Positional arguments: Required arguments in specific positions
  • Optional flags: Optional parameters with single/double dashes
  • Subcommands: Nested command structures
  • Environment variables: Configuration via environment
  • Config files: Persistent configuration storage
  • Validation: Type checking and value validation

2. Interactive Elements

  • Prompts: User input with validation
  • Confirmations: Yes/no confirmations
  • Selection menus: Choose from predefined options
  • Progress bars: Show operation progress
  • Spinners: Indicate ongoing work

3. User Experience

  • Help systems: Auto-generated help text
  • Error messages: Clear, actionable error reporting
  • Auto-completion: Tab completion for commands
  • Colors and formatting: Readable output formatting
  • Consistency: Follow CLI conventions

CLI Development Patterns

Go with Cobra Example

package main

import (
    "fmt"
    "os"
    "github.com/spf13/cobra"
    "github.com/spf13/viper"
)

var rootCmd = &cobra.Command{
    Use:   "myapp [command]",
    Short: "My application does awesome things",
    Long: `My application is a CLI tool that demonstrates 
best practices for command-line interface development.`,
}

var configCmd = &cobra.Command{
    Use:   "config [key] [value]",
    Short: "Get or set configuration values",
    Long:  `Get or set configuration values. If only key is provided,
gets the value. If both key and value are provided, sets the value.`,
    Args:  cobra.MinimumNArgs(1),
    Run:   runConfig,
}

var (
    configFile string
    verbose    bool
    output     string
)

func init() {
    cobra.OnInitialize(initConfig)
    
    rootCmd.PersistentFlags().StringVarP(&configFile, "config", "c", "", "config file (default is $HOME/.myapp.yaml)")
    rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "verbose output")
    rootCmd.PersistentFlags().StringVarP(&output, "output", "o", "json", "output format (json|yaml|text)")
    
    rootCmd.AddCommand(configCmd)
}

func initConfig() {
    if configFile != "" {
        viper.SetConfigFile(configFile)
    } else {
        home, err := os.UserHomeDir()
        if err != nil {
            fmt.Println(err)
            os.Exit(1)
        }
        
        viper.AddConfigPath(home)
        viper.SetConfigType("yaml")
        viper.SetConfigName(".myapp")
    }
    
    viper.AutomaticEnv()
    
    if err := viper.ReadInConfig(); err == nil {
        fmt.Println("Using config file:", viper.ConfigFileUsed())
    }
}

func runConfig(cmd *cobra.Command, args []string) {
    switch len(args) {
    case 1:
        // Get value
        value := viper.GetString(args[0])
        if value == "" {
            fmt.Printf("Config key '%s' not found\n", args[0])
            os.Exit(1)
        }
        fmt.Printf("%s: %s\n", args[0], value)
    case 2:
        // Set value
        viper.Set(args[0], args[1])
        fmt.Printf("Set %s = %s\n", args[0], args[1])
    default:
        fmt.Println("Usage: myapp config [key] [value]")
        os.Exit(1)
    }
}

func main() {
    if err := rootCmd.Execute(); err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
}

Python with Click Example

#!/usr/bin/env python3
import click
import json
import sys
from pathlib import Path

@click.group()
@click.option('--config', '-c', type=click.Path(), help='Configuration file path')
@click.option('--verbose', '-v', is_flag=True, help='Enable verbose output')
@click.pass_context
def cli(ctx, config, verbose):
    """My application does awesome things."""
    ctx.ensure_object(dict)
    ctx.obj['config'] = config
    ctx.obj['verbose'] = verbose

@cli.command()
@click.argument('filename', type=click.Path(exists=True))
@click.option('--format', '-f', 
              type=click.Choice(['json', 'yaml', 'text']), 
              default='text', 
              help='Output format')
@click.pass_context
def process(ctx, filename, format):
    """Process a file and output results."""
    verbose = ctx.obj.get('verbose', False)
    
    if verbose:
        click.echo(f"Processing file: {filename}")
    
    try:
        with open(filename, 'r') as f:
            content = f.read()
        
        # Process the content
        result = process_content(content)
        
        # Output in requested format
        if format == 'json':
            click.echo(json.dumps(result, indent=2))
        elif format == 'yaml':
            import yaml
            click.echo(yaml.dump(result))
        else:
            click.echo(str(result))
            
    except Exception as e:
        click.echo(f"Error: {e}", err=True)
        sys.exit(1)

@cli.command()
@click.argument('key')
@click.argument('value', required=False)
@click.pass_context
def config(ctx, key, value):
    """Get or set configuration values."""
    config_file = ctx.obj.get('config') or get_default_config_path()
    
    if value:
        set_config_value(config_file, key, value)
        click.echo(f"Set {key} = {value}")
    else:
        value = get_config_value(config_file, key)
        if value:
            click.echo(f"{key} = {value}")
        else:
            click.echo(f"Config key '{key}' not found")
            sys.exit(1)

def process_content(content):
    """Example content processing function."""
    lines = content.split('\n')
    return {
        'lines': len(lines),
        'chars': len(content),
        'words': len(content.split())
    }

if __name__ == '__main__':
    cli()

Rust with Clap Example

use clap::{Parser, Subcommand};
use serde::{Deserialize, Serialize};
use std::fs;
use std::io;

#[derive(Parser)]
#[command(author, version, about, long_about = None)]
struct Cli {
    #[arg(short, long, default_value = "config.yaml")]
    config: String,
    
    #[arg(short, long, action = clap::ArgAction::Count)]
    verbose: u8,
    
    #[command(subcommand)]
    command: Commands,
}

#[derive(Subcommand)]
enum Commands {
    Process(ProcessCommand),
    Config(ConfigCommand),
}

#[derive(Parser)]
struct ProcessCommand {
    /// Input file to process
    #[arg(value_name = "FILE")]
    file: String,
    
    /// Output format
    #[arg(short, long, default_value = "text")]
    format: String,
}

#[derive(Parser)]
struct ConfigCommand {
    /// Configuration key to get/set
    key: String,
    
    /// Configuration value to set
    value: Option<String>,
}

fn main() {
    let cli = Cli::parse();
    
    match cli.command {
        Commands::Process(cmd) => process_file(cmd, &cli),
        Commands::Config(cmd) => handle_config(cmd, &cli),
    }
}

fn process_file(cmd: ProcessCommand, cli: &Cli) {
    if cli.verbose > 0 {
        println!("Processing file: {}", cmd.file);
    }
    
    match fs::read_to_string(&cmd.file) {
        Ok(content) => {
            let result = analyze_content(&content);
            
            match cmd.format.as_str() {
                "json" => println!("{}", serde_json::to_string_pretty(&result).unwrap()),
                "yaml" => println!("{}", serde_yaml::to_string(&result).unwrap()),
                _ => println!("{:?}", result),
            }
        }
        Err(e) => {
            eprintln!("Error reading file: {}", e);
            std::process::exit(1);
        }
    }
}

fn handle_config(cmd: ConfigCommand, cli: &Cli) {
    match cmd.value {
        Some(value) => set_config_value(&cli.config, &cmd.key, &value),
        None => {
            match get_config_value(&cli.config, &cmd.key) {
                Some(value) => println!("{} = {}", cmd.key, value),
                None => {
                    eprintln!("Config key '{}' not found", cmd.key);
                    std::process::exit(1);
                }
            }
        }
    }
}

#[derive(Serialize, Deserialize)]
struct ContentAnalysis {
    lines: usize,
    chars: usize,
    words: usize,
}

fn analyze_content(content: &str) -> ContentAnalysis {
    ContentAnalysis {
        lines: content.lines().count(),
        chars: content.chars().count(),
        words: content.split_whitespace().count(),
    }
}

Interactive CLI Patterns

Confirmation Prompts (Python with Click)

import click

@click.command()
def deploy():
    """Deploy the application."""
    
    if not click.confirm('This will deploy to production. Continue?'):
        click.echo('Deployment cancelled.')
        return
    
    with click.progressbar(length=100, label='Deploying') as bar:
        for i in range(100):
            time.sleep(0.1)
            bar.update(1)
    
    click.echo('Deployment complete!')

@click.command()
@click.option('--force', is_flag=True, help='Skip confirmation')
def delete(force):
    """Delete resources."""
    
    if not force:
        if not click.confirm('This will delete all resources. Continue?'):
            click.echo('Deletion cancelled.')
            return
    
    # Perform deletion
    click.echo('Resources deleted.')

Interactive Selection (Node.js with Inquirer)

const inquirer = require('inquirer');
const program = require('commander');

program
    .version('1.0.0')
    .command('setup')
    .description('Interactive setup wizard')
    .action(async () => {
        const answers = await inquirer.prompt([
            {
                type: 'input',
                name: 'name',
                message: 'What is your project name?',
                validate: input => input.length > 0 || 'Project name is required'
            },
            {
                type: 'list',
                name: 'template',
                message: 'Choose a template:',
                choices: ['basic', 'advanced', 'minimal']
            },
            {
                type: 'checkbox',
                name: 'features',
                message: 'Select features:',
                choices: ['database', 'auth', 'logging', 'testing']
            }
        ]);
        
        console.log('Setup complete with:', answers);
        // Continue setup...
    });

program.parse(process.argv);

CLI Testing Patterns

Go CLI Testing

package main

import (
    "bytes"
    "os"
    "strings"
    "testing"
    "github.com/spf13/cobra"
)

func TestRootCommand(t *testing.T) {
    tests := []struct {
        name     string
        args     []string
        expected string
        error    bool
    }{
        {
            name:     "help flag",
            args:     []string{"--help"},
            expected: "myapp does awesome things",
            error:    false,
        },
        {
            name:     "invalid command",
            args:     []string{"invalid"},
            expected: "",
            error:    true,
        },
    }
    
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            // Capture output
            buf := new(bytes.Buffer)
            rootCmd.SetOut(buf)
            rootCmd.SetErr(buf)
            
            // Set arguments
            rootCmd.SetArgs(tt.args)
            
            // Execute command
            err := rootCmd.Execute()
            
            output := buf.String()
            
            if tt.error && err == nil {
                t.Errorf("expected error but got none")
            }
            if !tt.error && err != nil {
                t.Errorf("unexpected error: %v", err)
            }
            if !strings.Contains(output, tt.expected) {
                t.Errorf("expected output to contain %q, got %q", tt.expected, output)
            }
        })
    }
}

Python CLI Testing

import pytest
from click.testing import CliRunner
from myapp import cli

def test_process_command(tmp_path):
    """Test the process command."""
    runner = CliRunner()
    
    # Create test file
    test_file = tmp_path / "test.txt"
    test_file.write_text("test content\n")
    
    # Run command
    result = runner.invoke(cli.process, [str(test_file)])
    
    assert result.exit_code == 0
    assert "lines: 1" in result.output
    assert "chars: 12" in result.output

def test_config_command():
    """Test the config command."""
    runner = CliRunner()
    
    # Test getting value
    result = runner.invoke(cli.config, ['test.key'])
    assert result.exit_code == 0
    
    # Test setting value
    result = runner.invoke(cli.config, ['test.key', 'test.value'])
    assert result.exit_code == 0
    assert "Set test.key = test.value" in result.output

CLI Best Practices

1. Command Design

  • Use descriptive command names (verbs are good)
  • Follow Unix conventions (short options, long options)
  • Provide help text and examples
  • Support configuration files and environment variables
  • Handle errors gracefully

2. Output Formatting

  • Support multiple output formats (JSON, YAML, plain text)
  • Use colors sparingly and respect NO_COLOR environment
  • Provide progress indicators for long operations
  • Format numbers with appropriate units

3. User Experience

  • Implement auto-completion where possible
  • Provide clear error messages with suggestions
  • Use confirmation prompts for destructive operations
  • Support verbose and quiet modes

4. Distribution

  • Create single-binary executables where possible
  • Provide installation instructions
  • Include man pages or help documentation
  • Consider packaging for different platforms

When to Use This Skill

Use this skill when you need to:

  • Build command-line tools and utilities
  • Create CLI interfaces for existing applications
  • Design interactive command-line applications
  • Implement argument parsing and validation
  • Add help systems and documentation to CLI tools
  • Test CLI applications
  • Package and distribute CLI tools

Always prioritize:

  • Clear, intuitive command structures
  • Comprehensive error handling
  • Cross-platform compatibility
  • Rich user experience when appropriate
  • Comprehensive testing coverage
Skills similaires