"""Patterns command for detecting command sequences.""" from typing import Optional import click from rich.console import Console from rich.table import Table from shellhist.core import HistoryLoader from shellhist.core.patterns import ( detect_command_pairs, detect_command_triplets, detect_repetitive_commands, CommandPattern, ) @click.command("patterns") @click.option( "--history", "-H", type=str, help="Path to history file", ) @click.option( "--min-frequency", "-m", type=int, default=2, help="Minimum frequency for pattern detection (default: 2)", ) @click.option( "--type", "-t", type=click.Choice(["pairs", "triplets", "repetitive", "all"]), default="all", help="Type of patterns to detect", ) @click.option( "--shell", "-s", type=click.Choice(["bash", "zsh"]), help="Shell type for parsing", ) @click.pass_context def patterns_command( ctx: click.Context, history: Optional[str], min_frequency: int, type: str, shell: Optional[str], ) -> None: """Detect repetitive command patterns in your shell history. Examples: \b shellhist patterns shellhist patterns --min-frequency 3 shellhist patterns --type pairs shellhist patterns --type triplets """ 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 total_entries = len(store.entries) if type in ("pairs", "all"): pairs = detect_command_pairs(store, min_frequency=min_frequency) if type in ("triplets", "all"): triplets = detect_command_triplets(store, min_frequency=min_frequency) if type in ("repetitive", "all"): repetitive = detect_repetitive_commands(store, min_frequency=min_frequency) if type == "pairs": _display_patterns(console, "Command Pairs", pairs, total_entries) elif type == "triplets": _display_patterns(console, "Command Triplets", triplets, total_entries) elif type == "repetitive": _display_patterns(console, "Repetitive Commands", repetitive, total_entries) else: if pairs: _display_patterns(console, "Command Pairs", pairs, total_entries) if triplets: _display_patterns(console, "Command Triplets", triplets, total_entries) if repetitive: _display_patterns(console, "Repetitive Commands", repetitive, total_entries) if not pairs and not triplets and not repetitive: console.print( f"[yellow]No patterns found with minimum frequency {min_frequency}.[/yellow]" ) console.print("Try lowering the --min-frequency threshold.") except FileNotFoundError as e: console.print(f"[red]Error: {e}[/red]") ctx.exit(1) except Exception as e: console.print(f"[red]Error detecting patterns: {e}[/red]") ctx.exit(1) def _display_patterns( console: Console, title: str, patterns: list[CommandPattern], total_entries: int, ) -> None: """Display detected patterns in a table.""" if not patterns: return console.print(f"\n[bold cyan]{title}[/bold cyan]") table = Table(show_header=True, header_style="bold magenta") table.add_column("#", width=4) table.add_column("Count", width=8) table.add_column("%", width=8) table.add_column("Pattern", width=70) for i, pattern in enumerate(patterns[:30], 1): if hasattr(pattern, 'commands'): cmd_str = " -> ".join(pattern.commands) else: cmd_str = str(pattern) if len(cmd_str) > 68: cmd_str = cmd_str[:68] + "..." pct = f"{pattern.percentage:.1f}%" if hasattr(pattern, 'percentage') else "N/A" table.add_row( str(i), str(pattern.frequency), pct, cmd_str, ) console.print(table)