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**
```python
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**
```bash
**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**
```python
@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**
```bash
**Install with pip**
pip install -e .
**Or use pipx for isolated install**
pipx install .
**Create standalone binary with PyInstaller**
pyinstaller --onefile cli.py
```