Files
shell-history-automation-tool/shellhist/cli/patterns.py
7000pctAUTO 65f08d6534
Some checks failed
Shellhist CI / test (push) Has been cancelled
Shellhist CI / build (push) Has been cancelled
CI / test (push) Has been cancelled
fix: resolve CI type checking issues
- Add return type annotations to __hash__ (-> int) and __eq__ (-> bool) in HistoryEntry
- Add TextIO import and type annotations for file parameters
- Add type ignore comment for fuzzywuzzy import
- Add HistoryEntry import and list type annotations in time_analysis
- Add assert statements for Optional[datetime] timestamps
- Add TypedDict classes for type-safe pattern dictionaries
- Add CommandPattern import and list[CommandPattern] type annotation
- Add -> None return types to all test methods
- Remove unused HistoryEntry import (F401)
2026-01-31 14:19:15 +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,
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)