Typer builds CLIs from Python type annotations. No argparse, no manual parsing — just decorators and types.
Installation
uv add typer
Basic application
import typer
app = typer.Typer()
@app.command()
def greet(name: str, times: int = 1):
"""Greets the user."""
for _ in range(times):
typer.echo(f"Hello, {name}!")
if __name__ == "__main__":
app()
uv run python main.py Alice # Hello, Alice!
uv run python main.py Alice --times 3 # Three times
uv run python main.py --help # Auto-generated docs
Multiple commands
import typer
app = typer.Typer(help="AI agent CLI")
@app.command()
def chat(message: str = typer.Argument(..., help="Message for Claude")):
"""Send a message to Claude."""
typer.echo(f"Sending: {message}")
@app.command()
def history(limit: int = typer.Option(10, help="Number of messages")):
"""Show conversation history."""
typer.echo(f"Last {limit} messages")
@app.command()
def clear():
"""Clear the history."""
typer.echo("History cleared")
if __name__ == "__main__":
app()
uv run python main.py chat "Hello!"
uv run python main.py history --limit 5
uv run python main.py clear
Argument vs Option
@app.command()
def analyze(
text: str = typer.Argument(...), # required positional
model: str = typer.Option("claude-sonnet-4-6"), # --model
verbose: bool = typer.Option(False, "--verbose", "-v"), # flag
):
pass
Argument(...)— positional, requiredArgument("default")— positional with a default valueOption(default)— named--param value
Colors and formatting
from rich.console import Console
console = Console()
@app.command()
def status():
console.print("[bold green]✓[/] Agent running")
console.print("[bold red]✗[/] Connection error")
typer.echo(typer.style("Done", fg=typer.colors.GREEN, bold=True))
Interactive input
@app.command()
def setup():
api_key = typer.prompt("Enter your API key", hide_input=True)
confirm = typer.confirm("Save to .env?")
if confirm:
typer.echo("Saved!")
💬 Comments (0)
No comments yet
Be the first to share your opinion about this article!