Initial upload with CI/CD workflow
Some checks failed
CI / test (push) Has been cancelled

This commit is contained in:
2026-01-31 13:13:08 +00:00
parent 4add24159d
commit 2ac25a13ab

147
shellhist/cli/patterns.py Normal file
View File

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