"""Alias suggestion command.""" import os from typing import Optional import click from rich.console import Console from rich.panel import Panel from shellhist.core import HistoryLoader from shellhist.core.patterns import ( detect_command_pairs, detect_command_triplets, detect_repetitive_commands, ) @click.command("suggest-aliases") @click.option( "--history", "-H", type=str, help="Path to history file", ) @click.option( "--auto-create", "-a", is_flag=True, default=False, help="Automatically create aliases without prompting", ) @click.option( "--dry-run", "-d", is_flag=True, default=False, help="Show what would be created without making changes", ) @click.option( "--shell", "-s", type=click.Choice(["bash", "zsh"]), help="Shell type for parsing", ) @click.pass_context def alias_command( ctx: click.Context, history: Optional[str], auto_create: bool, dry_run: bool, shell: Optional[str], ) -> None: """Generate alias suggestions for detected patterns. Examples: \b shellhist suggest-aliases shellhist suggest-aliases --auto-create shellhist suggest-aliases --dry-run """ console = Console() try: loader = HistoryLoader(history_path=history) store = loader.load() if not store.entries: console.print("[yellow]No entries found in history.[/yellow]") return pairs = detect_command_pairs(store, min_frequency=2) triplets = detect_command_triplets(store, min_frequency=2) sequences = [ (p.commands, p.frequency) for p in pairs + triplets if len(p.commands) >= 2 ] if not sequences: console.print( "[yellow]No command sequences found for alias suggestions.[/yellow]" ) console.print("Try running more commands first.") return console.print(f"\n[bold cyan]Detected Command Sequences[/bold cyan]") for i, (cmds, freq) in enumerate(sequences[:10], 1): seq = " && ".join(cmds) console.print(f"{i}. {seq} (frequency: {freq})") console.print("\n[bold cyan]Suggested Aliases[/bold cyan]") for i, (cmds, freq) in enumerate(sequences[:10], 1): alias_name = generate_alias_name(cmds) alias_cmd = " && ".join(cmds) alias_str = f"alias {alias_name}='{alias_cmd}'" panel = Panel( f"[green]{alias_str}[/green]", title=f"Alias {i}", expand=False, ) console.print(panel) if not dry_run: if auto_create: append_to_shell_file(alias_str) console.print(f" [green]Created alias '{alias_name}'[/green]") else: if click.confirm(f" Create this alias?"): append_to_shell_file(alias_str) console.print(f" [green]Created alias '{alias_name}'[/green]") except FileNotFoundError as e: console.print(f"[red]Error: {e}[/red]") ctx.exit(1) except Exception as e: console.print(f"[red]Error generating aliases: {e}[/red]") ctx.exit(1) def generate_alias_name(commands: list[str]) -> str: """Generate a meaningful alias name from command list.""" if not commands: return "custom" keywords = [] for cmd in commands: parts = cmd.split() if parts: keywords.append(parts[0]) keywords = [k for k in keywords if k not in ("sudo", "do", "run", "exec")] if not keywords: return "custom" base = "".join(keywords[:3]).lower() if len(base) > 12: base = base[:12] return base def append_to_shell_file(alias_str: str) -> None: """Append alias to shell config file.""" shell = os.environ.get("SHELL", "") if "zsh" in shell: rc_file = os.path.expanduser("~/.zshrc") else: rc_file = os.path.expanduser("~/.bashrc") with open(rc_file, "a", encoding="utf-8") as f: f.write(f"\n{alias_str}\n")