Files
shell-history-automation-tool/shellhist/cli/patterns.py
7000pctAUTO 2ac25a13ab
Some checks failed
CI / test (push) Has been cancelled
Initial upload with CI/CD workflow
2026-01-31 13:13:08 +00:00

148 lines
4.2 KiB
Python

"""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)