Files
7000pctAUTO 7028371275
Some checks failed
CI / test (push) Has been cancelled
Shellhist CI / test (push) Has been cancelled
Shellhist CI / build (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:18 +00:00

160 lines
4.3 KiB
Python

"""Alias suggestion command."""
import os
from typing import Optional
import click
from rich.console import Console
from rich.panel import Panel
from shellhist.core import HistoryLoader
from shellhist.core.patterns import (
detect_command_pairs,
detect_command_triplets,
detect_repetitive_commands,
)
@click.command("suggest-aliases")
@click.option(
"--history",
"-H",
type=str,
help="Path to history file",
)
@click.option(
"--auto-create",
"-a",
is_flag=True,
default=False,
help="Automatically create aliases without prompting",
)
@click.option(
"--dry-run",
"-d",
is_flag=True,
default=False,
help="Show what would be created without making changes",
)
@click.option(
"--shell",
"-s",
type=click.Choice(["bash", "zsh"]),
help="Shell type for parsing",
)
@click.pass_context
def alias_command(
ctx: click.Context,
history: Optional[str],
auto_create: bool,
dry_run: bool,
shell: Optional[str],
) -> None:
"""Generate alias suggestions for detected patterns.
Examples:
\b
shellhist suggest-aliases
shellhist suggest-aliases --auto-create
shellhist suggest-aliases --dry-run
"""
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
pairs = detect_command_pairs(store, min_frequency=2)
triplets = detect_command_triplets(store, min_frequency=2)
sequences = [
(p.commands, p.frequency)
for p in pairs + triplets
if len(p.commands) >= 2
]
if not sequences:
console.print(
"[yellow]No command sequences found for alias suggestions.[/yellow]"
)
console.print("Try running more commands first.")
return
console.print(f"\n[bold cyan]Detected Command Sequences[/bold cyan]")
for i, (cmds, freq) in enumerate(sequences[:10], 1):
seq = " && ".join(cmds)
console.print(f"{i}. {seq} (frequency: {freq})")
console.print("\n[bold cyan]Suggested Aliases[/bold cyan]")
for i, (cmds, freq) in enumerate(sequences[:10], 1):
alias_name = generate_alias_name(cmds)
alias_cmd = " && ".join(cmds)
alias_str = f"alias {alias_name}='{alias_cmd}'"
panel = Panel(
f"[green]{alias_str}[/green]",
title=f"Alias {i}",
expand=False,
)
console.print(panel)
if not dry_run:
if auto_create:
append_to_shell_file(alias_str)
console.print(f" [green]Created alias '{alias_name}'[/green]")
else:
if click.confirm(f" Create this alias?"):
append_to_shell_file(alias_str)
console.print(f" [green]Created alias '{alias_name}'[/green]")
except FileNotFoundError as e:
console.print(f"[red]Error: {e}[/red]")
ctx.exit(1)
except Exception as e:
console.print(f"[red]Error generating aliases: {e}[/red]")
ctx.exit(1)
def generate_alias_name(commands: list[str]) -> str:
"""Generate a meaningful alias name from command list."""
if not commands:
return "custom"
keywords = []
for cmd in commands:
parts = cmd.split()
if parts:
keywords.append(parts[0])
keywords = [k for k in keywords if k not in ("sudo", "do", "run", "exec")]
if not keywords:
return "custom"
base = "".join(keywords[:3]).lower()
if len(base) > 12:
base = base[:12]
return base
def append_to_shell_file(alias_str: str) -> None:
"""Append alias to shell config file."""
shell = os.environ.get("SHELL", "")
if "zsh" in shell:
rc_file = os.path.expanduser("~/.zshrc")
else:
rc_file = os.path.expanduser("~/.bashrc")
with open(rc_file, "a", encoding="utf-8") as f:
f.write(f"\n{alias_str}\n")