# CLI Command Template

## Single Command Template
```python
#!/usr/bin/env python
"""CLI for processing data files."""

import sys
import logging
from pathlib import Path

import click

from mylib import process_file, Config
from mylib.exceptions import ProcessingError, ValidationError

# Setup logging
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)


@click.command()
@click.argument('input_file', type=click.Path(exists=True, path_type=Path))
@click.option('--output', '-o', type=click.Path(path_type=Path),
              help='Output file (default: stdout)')
@click.option('--format', '-f',
              type=click.Choice(['json', 'csv', 'yaml']),
              default='json',
              help='Output format')
@click.option('--verbose', '-v', is_flag=True, help='Verbose output')
@click.option('--quiet', '-q', is_flag=True, help='Suppress output')
@click.option('--config', '-c', type=click.Path(exists=True, path_type=Path),
              help='Configuration file')
def process(input_file, output, format, verbose, quiet, config):
    """Process INPUT_FILE and generate output.

    Examples:
        Process file to stdout:
        $ mycli process data.json

        Process to file with format:
        $ mycli process data.json -o result.csv -f csv

        Use custom config:
        $ mycli process data.json -c config.yaml
    """
    # Setup logging level
    if quiet:
        logging.getLogger().setLevel(logging.ERROR)
    elif verbose:
        logging.getLogger().setLevel(logging.DEBUG)

    # Load configuration
    if config:
        app_config = Config.from_file(config)
    else:
        app_config = Config()

    try:
        # Process file
        click.echo(f"Processing {input_file}...", err=True)
        result = process_file(input_file, config=app_config)

        # Format output
        if format == 'json':
            output_data = json.dumps(result, indent=2)
        elif format == 'csv':
            output_data = to_csv(result)
        elif format == 'yaml':
            output_data = yaml.dump(result)

        # Write output
        if output:
            output.write_text(output_data)
            click.echo(f"✓ Output written to {output}", err=True)
        else:
            click.echo(output_data)

    except ValidationError as e:
        click.echo(f"✗ Validation error: {e}", err=True)
        sys.exit(1)
    except ProcessingError as e:
        click.echo(f"✗ Processing failed: {e}", err=True)
        sys.exit(2)
    except Exception as e:
        logger.exception("Unexpected error")
        click.echo(f"✗ Unexpected error: {e}", err=True)
        sys.exit(3)


if __name__ == '__main__':
    process()
```

## Multi-Command CLI Template
```python
"""Multi-command CLI application."""

import click
from mylib import __version__

# Create CLI group
@click.group()
@click.version_option(version=__version__)
@click.option('--debug/--no-debug', default=False,
              help='Enable debug mode')
@click.pass_context
def cli(ctx, debug):
    """MyApp - A tool for processing data.

    Use --help with any command for more information.
    """
    # Store shared state in context
    ctx.ensure_object(dict)
    ctx.obj['DEBUG'] = debug

    if debug:
        click.echo("Debug mode enabled", err=True)


@cli.command()
@click.argument('input_files', nargs=-1, required=True,
                type=click.Path(exists=True))
@click.option('--workers', '-w', default=1, help='Number of workers')
@click.pass_context
def process(ctx, input_files, workers):
    """Process one or more INPUT_FILES.

    Examples:
        Process single file:
        $ myapp process data.json

        Process multiple files:
        $ myapp process *.json

        Process with parallel workers:
        $ myapp process *.json -w 4
    """
    debug = ctx.obj['DEBUG']

    with click.progressbar(input_files, label='Processing files') as files:
        for file in files:
            if debug:
                click.echo(f"Processing {file}", err=True)
            process_file(file, workers=workers)

    click.echo(f"✓ Processed {len(input_files)} files")


@cli.command()
@click.option('--all', '-a', is_flag=True, help='Show all information')
def status(all):
    """Show application status."""
    click.echo(f"MyApp v{__version__}")
    click.echo(f"Status: OK")

    if all:
        click.echo("\nDetailed Information:")
        click.echo(f"  Config: {Config.get_path()}")
        click.echo(f"  Cache: {Cache.size()} items")


@cli.group()
def config():
    """Manage configuration."""
    pass


@config.command('set')
@click.argument('key')
@click.argument('value')
def config_set(key, value):
    """Set configuration value."""
    cfg = Config.load()
    cfg.set(key, value)
    cfg.save()
    click.echo(f"✓ Set {key} = {value}")


@config.command('get')
@click.argument('key')
def config_get(key):
    """Get configuration value."""
    cfg = Config.load()
    value = cfg.get(key)
    if value is None:
        click.echo(f"Key '{key}' not found", err=True)
        sys.exit(1)
    click.echo(value)


@config.command('list')
def config_list():
    """List all configuration values."""
    cfg = Config.load()
    for key, value in cfg.items():
        click.echo(f"{key}: {value}")


# Entry point
if __name__ == '__main__':
    cli()
```

## Advanced CLI Patterns

### Interactive Prompts
```python
@cli.command()
def init():
    """Initialize a new project."""
    name = click.prompt('Project name')
    author = click.prompt('Author name', default=os.getenv('USER'))

    project_type = click.prompt(
        'Project type',
        type=click.Choice(['library', 'application']),
        default='library'
    )

    if click.confirm('Include tests?', default=True):
        test_framework = click.prompt(
            'Test framework',
            type=click.Choice(['pytest', 'unittest']),
            default='pytest'
        )

    # Create project...
    click.echo(f"✓ Created {project_type} project: {name}")
```

### Custom Validators
```python
def validate_email(ctx, param, value):
    """Validate email address."""
    if value and '@' not in value:
        raise click.BadParameter('Invalid email address')
    return value

@cli.command()
@click.option('--email', '-e', callback=validate_email,
              help='Email address')
def subscribe(email):
    """Subscribe to notifications."""
    if email:
        click.echo(f"✓ Subscribed {email}")
```

### Environment Variable Support
```python
@cli.command()
@click.option('--api-key',
              envvar='MYAPP_API_KEY',
              help='API key (or set MYAPP_API_KEY)')
@click.option('--endpoint',
              envvar='MYAPP_ENDPOINT',
              default='https://api.example.com',
              help='API endpoint')
def sync(api_key, endpoint):
    """Sync with remote API."""
    if not api_key:
        raise click.ClickException(
            "API key required. Set --api-key or MYAPP_API_KEY"
        )

    click.echo(f"Syncing with {endpoint}...")
```

### Output Formatting
```python
@cli.command()
@click.option('--format', '-f',
              type=click.Choice(['table', 'json', 'csv']),
              default='table')
def list_items(format):
    """List all items."""
    items = get_items()

    if format == 'table':
        # Pretty table output
        click.echo("ID  | Name       | Status")
        click.echo("----|------------|-------")
        for item in items:
            click.echo(f"{item.id:<4}| {item.name:<10} | {item.status}")

    elif format == 'json':
        click.echo(json.dumps([item.dict() for item in items], indent=2))

    elif format == 'csv':
        click.echo("id,name,status")
        for item in items:
            click.echo(f"{item.id},{item.name},{item.status}")
```
