geronimo.cli.utils

CLI utility functions for consistent output formatting.

Provides styled output helpers, spinners, and common patterns used across all CLI commands.

  1"""CLI utility functions for consistent output formatting.
  2
  3Provides styled output helpers, spinners, and common patterns used
  4across all CLI commands.
  5"""
  6
  7from contextlib import contextmanager
  8from typing import Optional
  9
 10from rich.console import Console
 11from rich.panel import Panel
 12from rich.progress import Progress, SpinnerColumn, TextColumn
 13from rich.table import Table
 14
 15# Shared console instance - use this instead of creating new Console() in each command
 16console = Console()
 17
 18
 19def success(message: str) -> None:
 20    """Print a success message with green checkmark."""
 21    console.print(f"[green]✓[/green] {message}")
 22
 23
 24def error(message: str, exit_code: Optional[int] = None) -> None:
 25    """Print an error message with red X.
 26    
 27    Args:
 28        message: Error message to display.
 29        exit_code: If provided, raises SystemExit with this code.
 30    """
 31    console.print(f"[red]✗[/red] {message}")
 32    if exit_code is not None:
 33        import typer
 34        raise typer.Exit(code=exit_code)
 35
 36
 37def warning(message: str) -> None:
 38    """Print a warning message with yellow icon."""
 39    console.print(f"[yellow]⚠[/yellow] {message}")
 40
 41
 42def info(message: str) -> None:
 43    """Print an info message with blue icon."""
 44    console.print(f"[blue]ℹ[/blue] {message}")
 45
 46
 47def dim(message: str) -> None:
 48    """Print dimmed text for secondary information."""
 49    console.print(f"[dim]{message}[/dim]")
 50
 51
 52def styled_panel(
 53    content: str,
 54    title: Optional[str] = None,
 55    style: str = "blue",
 56    border_style: Optional[str] = None,
 57) -> None:
 58    """Print content in a styled panel.
 59    
 60    Args:
 61        content: Panel content (can include rich markup).
 62        title: Optional panel title.
 63        style: Style for the content (default: blue).
 64        border_style: Border color (defaults to style).
 65    """
 66    console.print(Panel(
 67        content,
 68        title=title,
 69        border_style=border_style or style,
 70    ))
 71
 72
 73def create_table(
 74    title: str,
 75    columns: list[tuple[str, str]],
 76) -> Table:
 77    """Create a styled table with predefined column styles.
 78    
 79    Args:
 80        title: Table title.
 81        columns: List of (name, style) tuples for columns.
 82        
 83    Returns:
 84        Configured Table instance (call table.add_row() to add data).
 85        
 86    Example:
 87        table = create_table("API Keys", [
 88            ("ID", "dim"),
 89            ("Name", "cyan"),
 90            ("Status", "green"),
 91        ])
 92        table.add_row("key-1", "My Key", "active")
 93        console.print(table)
 94    """
 95    table = Table(title=title, show_header=True)
 96    for name, style in columns:
 97        table.add_column(name, style=style)
 98    return table
 99
100
101@contextmanager
102def status_spinner(message: str):
103    """Show a spinner while performing an operation.
104    
105    Args:
106        message: Status message to display.
107        
108    Example:
109        with status_spinner("Deploying project..."):
110            deploy_project()
111    """
112    with Progress(
113        SpinnerColumn(),
114        TextColumn("[progress.description]{task.description}"),
115        console=console,
116        transient=True,
117    ) as progress:
118        progress.add_task(description=message, total=None)
119        yield
120
121
122def confirm_action(
123    message: str,
124    default: bool = False,
125) -> bool:
126    """Prompt user for confirmation.
127    
128    Args:
129        message: Confirmation prompt.
130        default: Default value if user just presses Enter.
131        
132    Returns:
133        True if user confirmed, False otherwise.
134    """
135    import typer
136    return typer.confirm(message, default=default)
137
138
139def prompt(
140    message: str,
141    default: Optional[str] = None,
142    hide_input: bool = False,
143) -> str:
144    """Prompt user for input.
145    
146    Args:
147        message: Input prompt.
148        default: Default value if user just presses Enter.
149        hide_input: If True, hide input (for passwords/tokens).
150        
151    Returns:
152        User input string.
153    """
154    import typer
155    return typer.prompt(message, default=default, hide_input=hide_input)
console = <console width=161 None>
def success(message: str) -> None:
20def success(message: str) -> None:
21    """Print a success message with green checkmark."""
22    console.print(f"[green]✓[/green] {message}")

Print a success message with green checkmark.

def error(message: str, exit_code: Optional[int] = None) -> None:
25def error(message: str, exit_code: Optional[int] = None) -> None:
26    """Print an error message with red X.
27    
28    Args:
29        message: Error message to display.
30        exit_code: If provided, raises SystemExit with this code.
31    """
32    console.print(f"[red]✗[/red] {message}")
33    if exit_code is not None:
34        import typer
35        raise typer.Exit(code=exit_code)

Print an error message with red X.

Args: message: Error message to display. exit_code: If provided, raises SystemExit with this code.

def warning(message: str) -> None:
38def warning(message: str) -> None:
39    """Print a warning message with yellow icon."""
40    console.print(f"[yellow]⚠[/yellow] {message}")

Print a warning message with yellow icon.

def info(message: str) -> None:
43def info(message: str) -> None:
44    """Print an info message with blue icon."""
45    console.print(f"[blue]ℹ[/blue] {message}")

Print an info message with blue icon.

def dim(message: str) -> None:
48def dim(message: str) -> None:
49    """Print dimmed text for secondary information."""
50    console.print(f"[dim]{message}[/dim]")

Print dimmed text for secondary information.

def styled_panel( content: str, title: Optional[str] = None, style: str = 'blue', border_style: Optional[str] = None) -> None:
53def styled_panel(
54    content: str,
55    title: Optional[str] = None,
56    style: str = "blue",
57    border_style: Optional[str] = None,
58) -> None:
59    """Print content in a styled panel.
60    
61    Args:
62        content: Panel content (can include rich markup).
63        title: Optional panel title.
64        style: Style for the content (default: blue).
65        border_style: Border color (defaults to style).
66    """
67    console.print(Panel(
68        content,
69        title=title,
70        border_style=border_style or style,
71    ))

Print content in a styled panel.

Args: content: Panel content (can include rich markup). title: Optional panel title. style: Style for the content (default: blue). border_style: Border color (defaults to style).

def create_table(title: str, columns: list[tuple[str, str]]) -> rich.table.Table:
74def create_table(
75    title: str,
76    columns: list[tuple[str, str]],
77) -> Table:
78    """Create a styled table with predefined column styles.
79    
80    Args:
81        title: Table title.
82        columns: List of (name, style) tuples for columns.
83        
84    Returns:
85        Configured Table instance (call table.add_row() to add data).
86        
87    Example:
88        table = create_table("API Keys", [
89            ("ID", "dim"),
90            ("Name", "cyan"),
91            ("Status", "green"),
92        ])
93        table.add_row("key-1", "My Key", "active")
94        console.print(table)
95    """
96    table = Table(title=title, show_header=True)
97    for name, style in columns:
98        table.add_column(name, style=style)
99    return table

Create a styled table with predefined column styles.

Args: title: Table title. columns: List of (name, style) tuples for columns.

Returns: Configured Table instance (call table.add_row() to add data).

Example: table = create_table("API Keys", [ ("ID", "dim"), ("Name", "cyan"), ("Status", "green"), ]) table.add_row("key-1", "My Key", "active") console.print(table)

@contextmanager
def status_spinner(message: str):
102@contextmanager
103def status_spinner(message: str):
104    """Show a spinner while performing an operation.
105    
106    Args:
107        message: Status message to display.
108        
109    Example:
110        with status_spinner("Deploying project..."):
111            deploy_project()
112    """
113    with Progress(
114        SpinnerColumn(),
115        TextColumn("[progress.description]{task.description}"),
116        console=console,
117        transient=True,
118    ) as progress:
119        progress.add_task(description=message, total=None)
120        yield

Show a spinner while performing an operation.

Args: message: Status message to display.

Example: with status_spinner("Deploying project..."): deploy_project()

def confirm_action(message: str, default: bool = False) -> bool:
123def confirm_action(
124    message: str,
125    default: bool = False,
126) -> bool:
127    """Prompt user for confirmation.
128    
129    Args:
130        message: Confirmation prompt.
131        default: Default value if user just presses Enter.
132        
133    Returns:
134        True if user confirmed, False otherwise.
135    """
136    import typer
137    return typer.confirm(message, default=default)

Prompt user for confirmation.

Args: message: Confirmation prompt. default: Default value if user just presses Enter.

Returns: True if user confirmed, False otherwise.

def prompt( message: str, default: Optional[str] = None, hide_input: bool = False) -> str:
140def prompt(
141    message: str,
142    default: Optional[str] = None,
143    hide_input: bool = False,
144) -> str:
145    """Prompt user for input.
146    
147    Args:
148        message: Input prompt.
149        default: Default value if user just presses Enter.
150        hide_input: If True, hide input (for passwords/tokens).
151        
152    Returns:
153        User input string.
154    """
155    import typer
156    return typer.prompt(message, default=default, hide_input=hide_input)

Prompt user for input.

Args: message: Input prompt. default: Default value if user just presses Enter. hide_input: If True, hide input (for passwords/tokens).

Returns: User input string.