#!/usr/bin/env python3 """ Project Jarvis — Command Line Interface Interactive CLI for testing Jarvis from the terminal. Supports both text and voice modes. """ import sys import os import time # Add backend to path sys.path.insert(0, os.path.dirname(__file__)) from rich.console import Console from rich.panel import Panel from rich.text import Text from rich.table import Table from rich.live import Live from rich import box console = Console() def print_banner(): banner = """ ╔══════════════════════════════════════════════════╗ ║ ║ ║ ██╗ █████╗ ██████╗ ██╗ ██╗██╗███████╗ ║ ║ ██║██╔══██╗██╔══██╗██║ ██║██║██╔════╝ ║ ║ ██║███████║██████╔╝██║ ██║██║███████╗ ║ ║ ██ ██║██╔══██║██╔══██╗╚██╗ ██╔╝██║╚════██║ ║ ║ ╚█████╔╝██║ ██║██║ ██║ ╚████╔╝ ██║███████║ ║ ║ ╚════╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═══╝ ╚═╝╚══════╝ ║ ║ ║ ║ Personal AI Assistant — 100% Local ║ ║ ║ ╚══════════════════════════════════════════════════╝ """ console.print(banner, style="bold blue") def init_services(): """Initialize all Jarvis services.""" console.print("\n[bold yellow]Initializing Jarvis...[/bold yellow]\n") from app.core import brain from config import settings table = Table(box=box.ROUNDED, title="Service Status", title_style="bold") table.add_column("Service", style="cyan") table.add_column("Status", style="green") table.add_column("Detail") try: brain.init() statuses = brain.get_all_status() for svc in statuses: status_style = { "ready": "[green]✓ Ready[/green]", "error": "[red]✗ Error[/red]", "not_loaded": "[yellow]◌ Not loaded[/yellow]", "unavailable": "[dim]— Unavailable[/dim]", }.get(svc["status"], f"[dim]{svc['status']}[/dim]") table.add_row(svc["name"].upper(), status_style, svc.get("detail", "")) console.print(table) console.print() return True except Exception as e: console.print(f"[bold red]Initialization error: {e}[/bold red]") return False def text_mode(): """Interactive text command mode.""" from app.core import brain console.print("[bold green]Text mode active.[/bold green] Type your commands below.") console.print("[dim]Type 'quit' to exit, 'voice' to switch to voice mode, 'status' for service status.[/dim]\n") session_id = "cli-session" while True: try: user_input = console.input("[bold cyan]You:[/bold cyan] ").strip() if not user_input: continue if user_input.lower() in ("quit", "exit", "q"): console.print("[dim]Goodbye, sir.[/dim]") break if user_input.lower() == "voice": voice_mode() continue if user_input.lower() == "status": show_status() continue if user_input.lower() == "clear": console.clear() print_banner() continue # Process the command start = time.time() result = brain.process_text(user_input, session_id=session_id) elapsed = (time.time() - start) * 1000 response = result.get("response_text", "I couldn't process that.") intent = result.get("intent", "unknown") console.print() console.print(f"[bold magenta]Jarvis:[/bold magenta] {response}") console.print(f"[dim] intent: {intent} | {elapsed:.0f}ms[/dim]\n") except KeyboardInterrupt: console.print("\n[dim]Interrupted. Type 'quit' to exit.[/dim]") except EOFError: break def voice_mode(): """Voice command mode — records, transcribes, processes, speaks.""" from app.core import brain from app.services import tts console.print("\n[bold green]Voice mode active.[/bold green] Speak after the prompt.") console.print("[dim]Press Ctrl+C to stop and return to text mode.[/dim]\n") session_id = "cli-voice" while True: try: console.print("[bold yellow]🎤 Listening...[/bold yellow] (speak now)") # Record and process result = brain.voice_loop_once() transcription = result.get("transcription", "") response = result.get("response_text", "") intent = result.get("intent", "unknown") if transcription: console.print(f"[bold cyan]You said:[/bold cyan] {transcription}") console.print(f"[bold magenta]Jarvis:[/bold magenta] {response}") console.print(f"[dim] intent: {intent} | {result.get('timing', {}).get('total_ms', 0):.0f}ms[/dim]\n") except KeyboardInterrupt: console.print("\n[dim]Returning to text mode...[/dim]\n") break def show_status(): """Show service status.""" from app.core import brain table = Table(box=box.ROUNDED, title="Service Status") table.add_column("Service", style="cyan") table.add_column("Status") table.add_column("Detail") for svc in brain.get_all_status(): status_icon = {"ready": "✓", "error": "✗", "unavailable": "—"}.get(svc["status"], "?") status_style = {"ready": "green", "error": "red", "unavailable": "dim"}.get(svc["status"], "yellow") table.add_row( svc["name"].upper(), f"[{status_style}]{status_icon} {svc['status']}[/{status_style}]", svc.get("detail", ""), ) console.print(table) console.print() def main(): print_banner() if not init_services(): console.print("[bold red]Failed to initialize. Make sure Ollama is running.[/bold red]") console.print("[dim]Run: ollama serve[/dim]") sys.exit(1) # Check for command line args if len(sys.argv) > 1: if sys.argv[1] == "--voice": voice_mode() return else: # Process single command from app.core import brain text = " ".join(sys.argv[1:]) result = brain.process_text(text) console.print(f"[bold magenta]Jarvis:[/bold magenta] {result['response_text']}") return # Default: interactive text mode text_mode() if __name__ == "__main__": main()