Cli

Building AI Command-Line Tools

CLI Frameworks for Python

Popular Libraries
| Library | Highlights | Complexity |
|---------|------------|------------|
| Click | Decorators, composable | Medium |
| Typer | Modern, type hints | Easy |
| argparse | Built-in, no deps | Medium |
| Fire | Auto-generate from functions | Very easy |
| Rich | Beautiful terminal output | Addon |

Building an LLM CLI with Typer

Basic Structure

import typer
from rich.console import Console
from openai import OpenAI

app = typer.Typer(help="AI Command Line Assistant")
console = Console()
client = OpenAI()

@app.command()
def ask(
    prompt: str = typer.Argument(..., help="Your question"),
    model: str = typer.Option("gpt-4o", "--model", "-m"),
    stream: bool = typer.Option(True, "--stream/--no-stream"),
):
    """Ask the AI a question."""
    response = client.chat.completions.create(
        model=model,
        messages=[{"role": "user", "content": prompt}],
        stream=stream,
    )

    if stream:
        for chunk in response:
            if chunk.choices[0].delta.content:
                console.print(chunk.choices[0].delta.content, end="")
        console.print()
    else:
        console.print(response.choices[0].message.content)

@app.command()
def translate(
    text: str = typer.Argument(...),
    target: str = typer.Option("English", "--to", "-t"),
):
    """Translate text to target language."""
    response = client.chat.completions.create(
        model="gpt-4o",
        messages=[
            {"role": "system", "content": f"Translate to {target}. Only output the translation."},
            {"role": "user", "content": text}
        ],
    )
    console.print(response.choices[0].message.content)

if __name__ == "__main__":
    app()

Usage Examples

**Ask a question**
llm ask "What is the capital of France?"

**Use specific model**
llm ask "Explain attention" --model gpt-4o-mini

**Translate text**
llm translate "Hello, world!" --to Spanish

CLI Best Practices

User Experience
| Pattern | Implementation |
|---------|----------------|
| Progress | Rich progress bars for long operations |
| Streaming | Show output as it generates |
| Colors | Use for status (red=error, green=success) |
| Help | Clear documentation for all options |
| Defaults | Sensible defaults for common cases |

Error Handling

@app.command()
def summarize(file: Path = typer.Argument(...)):
    if not file.exists():
        console.print(f"[red]Error:[/red] File not found: {file}")
        raise typer.Exit(1)

    try:
        content = file.read_text()
**Process with LLM...**
    except Exception as e:
        console.print(f"[red]Error:[/red] {e}")
        raise typer.Exit(1)

Distribution

**Install with pip**
pip install -e .

**Or use pipx for isolated install**
pipx install .

**Create standalone binary with PyInstaller**
pyinstaller --onefile cli.py

Take cli further

Ask the copilot about this term, or have our engineers assess it against your process.