diff --git a/confsync/cli/detect.py b/confsync/cli/detect.py new file mode 100644 index 0000000..b793766 --- /dev/null +++ b/confsync/cli/detect.py @@ -0,0 +1,180 @@ +"""Detect command for configuration file detection.""" + +from typing import Dict, Optional, List +import typer +from rich.console import Console +from rich.table import Table +from rich.panel import Panel + +from confsync.detectors.base import DetectorRegistry +from confsync.models.config_models import ConfigCategory, ConfigFile + +detect_cmd = typer.Typer( + name="detect", + help="Detect and catalog configuration files", + no_args_is_help=True, +) + +console = Console() + + +@detect_cmd.command("all") +def detect_all( + category: Optional[str] = typer.Option( + None, "--category", "-c", help="Filter by category (editor, terminal, shell, git, ssh, docker, tmux, misc)" + ), + tool: Optional[str] = typer.Option( + None, "--tool", "-t", help="Filter by tool name" + ), + verbose: bool = typer.Option(False, "--verbose", "-v", help="Show detailed information"), +): + """Detect all configuration files.""" + categories = None + if category: + try: + categories = [ConfigCategory(category.lower())] + except ValueError: + console.print(f"[red]Error:[/red] Unknown category '{category}'") + console.print(f"Available categories: {', '.join([c.value for c in ConfigCategory])}") + return + + configs = DetectorRegistry.detect_all(categories) + + if tool: + configs = [c for c in configs if c.tool_name.lower() == tool.lower()] + + if not configs: + console.print("[yellow]No configuration files detected.[/yellow]") + return + + table = Table(title="Detected Configuration Files") + table.add_column("Tool", style="cyan") + table.add_column("Category", style="magenta") + table.add_column("File Path", style="green") + table.add_column("Size", justify="right", style="yellow") + + for config in configs: + table.add_row( + config.tool_name, + config.category.value, + config.path if len(config.path) < 50 else "..." + config.path[-47:], + f"{config.size} bytes" if verbose else str(config.size), + ) + + console.print(Panel.fit( + f"[bold]Detected {len(configs)} configuration files[/bold]", + style="blue", + )) + console.print(table) + + if verbose: + by_category: Dict[str, int] = {} + by_tool: Dict[str, int] = {} + for config in configs: + cat = config.category.value + by_category[cat] = by_category.get(cat, 0) + 1 + tool = config.tool_name + by_tool[tool] = by_tool.get(tool, 0) + 1 + + console.print("\n[bold]By Category:[/bold]") + for cat, count in by_category.items(): + console.print(f" {cat}: {count}") + + console.print("\n[bold]By Tool:[/bold]") + for tool, count in by_tool.items(): + console.print(f" {tool}: {count}") + + +@detect_cmd.command("editor") +def detect_editors( + verbose: bool = typer.Option(False, "--verbose", "-v", help="Show detailed information"), +): + """Detect editor configuration files.""" + configs = DetectorRegistry.detect_all([ConfigCategory.EDITOR]) + + if not configs: + console.print("[yellow]No editor configuration files detected.[/yellow]") + return + + _display_configs(configs, "Editor", verbose) + + +@detect_cmd.command("terminal") +def detect_terminal( + verbose: bool = typer.Option(False, "--verbose", "-v", help="Show detailed information"), +): + """Detect terminal configuration files.""" + configs = DetectorRegistry.detect_all([ConfigCategory.TERMINAL]) + + if not configs: + console.print("[yellow]No terminal configuration files detected.[/yellow]") + return + + _display_configs(configs, "Terminal", verbose) + + +@detect_cmd.command("shell") +def detect_shell( + verbose: bool = typer.Option(False, "--verbose", "-v", help="Show detailed information"), +): + """Detect shell configuration files.""" + configs = DetectorRegistry.detect_all([ConfigCategory.SHELL]) + + if not configs: + console.print("[yellow]No shell configuration files detected.[/yellow]") + return + + _display_configs(configs, "Shell", verbose) + + +@detect_cmd.command("git") +def detect_git( + verbose: bool = typer.Option(False, "--verbose", "-v", help="Show detailed information"), +): + """Detect Git configuration files.""" + configs = DetectorRegistry.detect_all([ConfigCategory.GIT]) + + if not configs: + console.print("[yellow]No Git configuration files detected.[/yellow]") + return + + _display_configs(configs, "Git", verbose) + + +@detect_cmd.command("tools") +def list_tools(): + """List all available detection tools.""" + tools = DetectorRegistry.list_tools() + categories = DetectorRegistry.list_categories() + + console.print("[bold]Available Detection Tools:[/bold]") + for tool in sorted(tools): + detector = DetectorRegistry.get_detector(tool) + if detector: + console.print(f" [cyan]{tool}[/cyan] ({detector.category.value})") + + console.print("\n[bold]Available Categories:[/bold]") + for cat in categories: + console.print(f" [magenta]{cat.value}[/magenta]") + + +def _display_configs(configs: List[ConfigFile], title: str, verbose: bool): + """Display detected configurations in a table.""" + table = Table(title=f"Detected {title} Configuration Files") + table.add_column("Tool", style="cyan") + table.add_column("File Path", style="green") + table.add_column("Size", justify="right", style="yellow") + + for config in configs: + path_display = config.path if len(config.path) < 60 else "..." + config.path[-57:] + table.add_row( + config.tool_name, + path_display, + f"{config.size} bytes" if verbose else str(config.size), + ) + + console.print(Panel.fit( + f"[bold]Detected {len(configs)} {title} configuration files[/bold]", + style="blue", + )) + console.print(table)