diff --git a/shell_speak/output.py b/shell_speak/output.py new file mode 100644 index 0000000..05c9a53 --- /dev/null +++ b/shell_speak/output.py @@ -0,0 +1,122 @@ +"""Output formatting with Rich.""" + +from typing import Optional + +from rich.console import Console +from rich.panel import Panel +from rich.syntax import Syntax +from rich.text import Text +from rich.theme import Theme + +from shell_speak.models import CommandMatch, HistoryEntry +from shell_speak.nlp import tokenize + + +custom_theme = Theme({ + "command": "bold cyan", + "keyword": "bold green", + "tool": "bold magenta", + "explanation": "italic", + "error": "bold red", + "warning": "yellow", + "success": "bold green", + "info": "blue", +}) + + +console = Console(theme=custom_theme) + + +def display_command(match: CommandMatch, explain: bool = False) -> None: + """Display a command match with rich formatting.""" + syntax = Syntax(match.command, "bash", theme="monokai", line_numbers=False) + + title = f"[tool]{match.pattern.tool}[/tool] command" + panel = Panel( + syntax, + title=title, + expand=False, + border_style="cyan", + ) + console.print(panel) + + if explain or match.confidence < 0.8: + confidence_pct = int(match.confidence * 100) + confidence_color = "success" if match.confidence >= 0.8 else "warning" if match.confidence >= 0.5 else "error" + console.print(f"Confidence: [{confidence_color}]{confidence_pct}%[/]") + + if match.explanation: + console.print(f"\n[explanation]{match.explanation}[/]") + + if explain: + _show_detailed_explanation(match) + + +def _show_detailed_explanation(match: CommandMatch) -> None: + """Show detailed breakdown of a command.""" + console.print("\n[info]Command breakdown:[/]") + tokens = tokenize(match.command) + + for token in tokens: + if token in ("docker", "kubectl", "git", "ls", "cd", "cat", "grep", "find", "rm", "cp", "mv"): + console.print(f" [keyword]{token}[/]", end=" ") + else: + console.print(f" {token}", end=" ") + + +def display_error(message: str) -> None: + """Display an error message.""" + console.print(f"[error]Error:[/] {message}") + + +def display_warning(message: str) -> None: + """Display a warning message.""" + console.print(f"[warning]Warning:[/] {message}") + + +def display_info(message: str) -> None: + """Display an info message.""" + console.print(f"[info]{message}[/]") + + +def display_history(entries: list[HistoryEntry], limit: int = 20) -> None: + """Display command history.""" + console.print(f"\n[info]Command History (last {limit}):[/]\n") + + for i, entry in enumerate(entries[-limit:], 1): + timestamp = entry.timestamp.strftime("%Y-%m-%d %H:%M") + console.print(f"{i}. [tool]{entry.tool}[/tool] | {timestamp}") + console.print(f" Query: {entry.query}") + console.print(f" [command]{entry.command}[/]") + console.print() + + +def display_suggestions(suggestions: list[str]) -> None: + """Display command suggestions.""" + if not suggestions: + return + + console.print("\n[info]Did you mean?[/]") + for i, suggestion in enumerate(suggestions[:5], 1): + console.print(f" {i}. {suggestion}") + + +def display_learn_success(query: str, command: str) -> None: + """Display confirmation of learning.""" + console.print(f"[success]Learned new command:[/]") + console.print(f" Query: {query}") + console.print(f" [command]{command}[/]") + + +def display_forget_success(query: str) -> None: + """Display confirmation of forgetting a pattern.""" + console.print(f"[success]Forgot pattern for:[/] {query}") + + +def display_help_header() -> None: + """Display the help header.""" + console.print(Panel( + Text("Shell Speak - Natural Language to Shell Commands", justify="center", style="bold cyan"), + subtitle="Type a description of what you want to do", + expand=False, + ))