From 2ac25a13ab5389a33ab6acbfbfbb66d0d1d8e0e3 Mon Sep 17 00:00:00 2001 From: 7000pctAUTO Date: Sat, 31 Jan 2026 13:13:08 +0000 Subject: [PATCH] Initial upload with CI/CD workflow --- shellhist/cli/patterns.py | 147 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 shellhist/cli/patterns.py diff --git a/shellhist/cli/patterns.py b/shellhist/cli/patterns.py new file mode 100644 index 0000000..d65486f --- /dev/null +++ b/shellhist/cli/patterns.py @@ -0,0 +1,147 @@ +"""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, + ngram_analysis, +) + + +@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, + 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)