Some checks failed
Shell History Alias Generator CI / test (push) Has been cancelled
280 lines
8.0 KiB
Python
280 lines
8.0 KiB
Python
"""CLI interface for Shell History Alias Generator."""
|
|
|
|
import sys
|
|
from pathlib import Path
|
|
from typing import List, Optional
|
|
|
|
import click
|
|
from rich.console import Console
|
|
from rich.panel import Panel
|
|
from rich.text import Text
|
|
|
|
from .parsers import HistoryParserFactory, BashHistoryParser, ZshHistoryParser, FishHistoryParser
|
|
from .command_analyzer import CommandAnalyzer
|
|
from .interactive_ui import InteractiveUI
|
|
from .export_manager import ExportManager
|
|
|
|
|
|
console = Console()
|
|
|
|
|
|
@click.group()
|
|
@click.version_option(version="0.1.0")
|
|
def main():
|
|
"""Shell History Alias Generator - Analyze shell history and create aliases."""
|
|
pass
|
|
|
|
|
|
@main.command()
|
|
@click.argument(
|
|
'history_file',
|
|
type=click.Path(exists=True, file_okay=True, dir_okay=False, path_type=Path),
|
|
required=False
|
|
)
|
|
@click.option(
|
|
'--shell',
|
|
type=click.Choice(['bash', 'zsh', 'fish', 'auto']),
|
|
default='auto',
|
|
help='Shell type for history parsing'
|
|
)
|
|
@click.option(
|
|
'--min-length',
|
|
type=int,
|
|
default=15,
|
|
help='Minimum command length to consider'
|
|
)
|
|
@click.option(
|
|
'--min-frequency',
|
|
type=int,
|
|
default=2,
|
|
help='Minimum command frequency to consider'
|
|
)
|
|
@click.option(
|
|
'--export',
|
|
type=click.Choice(['bash', 'zsh', 'fish']),
|
|
default=None,
|
|
help='Export format for generated aliases'
|
|
)
|
|
@click.option(
|
|
'--output',
|
|
type=click.Path(path_type=Path),
|
|
default=None,
|
|
help='Output file path for export'
|
|
)
|
|
@click.option(
|
|
'--non-interactive',
|
|
is_flag=True,
|
|
default=False,
|
|
help='Skip interactive review mode'
|
|
)
|
|
def analyze(
|
|
history_file: Optional[Path],
|
|
shell: str,
|
|
min_length: int,
|
|
min_frequency: int,
|
|
export: Optional[str],
|
|
output: Optional[Path],
|
|
non_interactive: bool
|
|
):
|
|
"""Analyze shell history and generate alias suggestions."""
|
|
console.print(Panel.fit(
|
|
Text("Shell History Alias Generator", style="bold magenta"),
|
|
subtitle="Analyzing your command history..."
|
|
))
|
|
|
|
parser = _get_parser(shell, history_file)
|
|
|
|
if parser is None:
|
|
console.print("[red]Error: Could not determine shell type.[/red]")
|
|
sys.exit(1)
|
|
|
|
if history_file:
|
|
commands = parser.parse_file(str(history_file))
|
|
else:
|
|
commands = parser.parse_all_history_files() if hasattr(parser, 'parse_all_history_files') else []
|
|
if not commands:
|
|
default_paths = parser.get_default_history_locations()
|
|
if default_paths:
|
|
if isinstance(default_paths, list) and default_paths:
|
|
commands = parser.parse_file(default_paths[0]) if isinstance(default_paths[0], str) else []
|
|
else:
|
|
commands = []
|
|
|
|
if not commands:
|
|
console.print("[yellow]No commands found in history.[/yellow]")
|
|
sys.exit(0)
|
|
|
|
console.print(f"[dim]Parsed {len(commands)} commands[/dim]")
|
|
|
|
analyzer = CommandAnalyzer()
|
|
suggestions = analyzer.analyze_commands(commands)
|
|
|
|
if not suggestions:
|
|
console.print("[yellow]No alias suggestions generated.[/yellow]")
|
|
sys.exit(0)
|
|
|
|
if non_interactive:
|
|
selected = suggestions
|
|
else:
|
|
ui = InteractiveUI()
|
|
ui.display_suggestions(suggestions, "Generated Alias Suggestions")
|
|
|
|
console.print("\n[bold]Reviewing suggestions...[/bold]")
|
|
selected = ui.run_review_session(suggestions)
|
|
|
|
if not selected:
|
|
console.print("[yellow]No aliases selected.[/yellow]")
|
|
sys.exit(0)
|
|
|
|
ui.display_summary(selected)
|
|
|
|
if not ui.confirm_selection():
|
|
console.print("[yellow]Cancelled.[/yellow]")
|
|
sys.exit(0)
|
|
|
|
if export:
|
|
export_manager = ExportManager()
|
|
if output:
|
|
content = export_manager.export(selected, export, str(output))
|
|
console.print(f"[green]Exported to {output}[/green]")
|
|
else:
|
|
content = export_manager.export(selected, export)
|
|
console.print("\n[bold]Generated aliases:[/bold]")
|
|
console.print(content)
|
|
else:
|
|
_print_selected_aliases(selected)
|
|
|
|
|
|
@main.command()
|
|
@click.argument(
|
|
'history_file',
|
|
type=click.Path(exists=True, file_okay=True, dir_okay=False, path_type=Path),
|
|
required=False
|
|
)
|
|
@click.option(
|
|
'--shell',
|
|
type=click.Choice(['bash', 'zsh', 'fish', 'auto']),
|
|
default='auto',
|
|
help='Shell type for history parsing'
|
|
)
|
|
@click.option(
|
|
'--format',
|
|
'output_format',
|
|
type=click.Choice(['bash', 'zsh', 'fish']),
|
|
default='bash',
|
|
help='Export format'
|
|
)
|
|
@click.option(
|
|
'--append/--replace',
|
|
default=False,
|
|
help='Append to rcfile or replace aliases section'
|
|
)
|
|
def install(
|
|
history_file: Optional[Path],
|
|
shell: str,
|
|
output_format: str,
|
|
append: bool
|
|
):
|
|
"""Install aliases directly to shell rc file."""
|
|
parser = _get_parser(shell, history_file)
|
|
|
|
if parser is None:
|
|
console.print("[red]Error: Could not determine shell type.[/red]")
|
|
sys.exit(1)
|
|
|
|
if history_file:
|
|
commands = parser.parse_file(str(history_file))
|
|
else:
|
|
commands = parser.parse_all_history_files() if hasattr(parser, 'parse_all_history_files') else []
|
|
|
|
if not commands:
|
|
console.print("[yellow]No commands found in history.[/yellow]")
|
|
sys.exit(0)
|
|
|
|
analyzer = CommandAnalyzer()
|
|
suggestions = analyzer.analyze_commands(commands)
|
|
|
|
if not suggestions:
|
|
console.print("[yellow]No alias suggestions generated.[/yellow]")
|
|
sys.exit(0)
|
|
|
|
rcfile = _get_rcfile_path(output_format)
|
|
|
|
if rcfile is None or not rcfile.exists():
|
|
console.print(f"[red]Could not find {output_format} rc file.[/red]")
|
|
sys.exit(1)
|
|
|
|
export_manager = ExportManager()
|
|
if export_manager.export_to_rcfile(suggestions, output_format, str(rcfile)):
|
|
console.print(f"[green]Aliases added to {rcfile}[/green]")
|
|
console.print(f"[dim]Restart your shell or run: source {rcfile}[/dim]")
|
|
else:
|
|
console.print("[red]Failed to write to rc file.[/red]")
|
|
sys.exit(1)
|
|
|
|
|
|
@main.command()
|
|
def shells():
|
|
"""List supported shell types."""
|
|
shells_list = HistoryParserFactory.get_supported_shells()
|
|
console.print("Supported shell types:")
|
|
for shell in shells_list:
|
|
console.print(f" - {shell}")
|
|
|
|
|
|
@main.command()
|
|
def locations():
|
|
"""Show default history file locations."""
|
|
console.print("Default history file locations:\n")
|
|
|
|
for shell_name in ['bash', 'zsh', 'fish']:
|
|
parser = HistoryParserFactory.get_parser(shell_name)
|
|
if parser:
|
|
locations = parser.get_default_history_locations()
|
|
console.print(f"[bold]{shell_name.upper()}[/bold]:")
|
|
for loc in locations:
|
|
exists = "[green]✓[/green]" if Path(loc).exists() else "[red]✗[/red]"
|
|
console.print(f" {exists} {loc}")
|
|
console.print()
|
|
|
|
|
|
def _get_parser(shell: str, history_file: Optional[Path]) -> Optional[HistoryParser]:
|
|
"""Get appropriate history parser."""
|
|
if shell == 'auto' and history_file:
|
|
detected = HistoryParserFactory.detect_shell_from_file(str(history_file))
|
|
if detected:
|
|
shell = detected
|
|
|
|
if shell == 'auto':
|
|
shell = 'bash'
|
|
|
|
return HistoryParserFactory.get_parser(shell)
|
|
|
|
|
|
def _get_rcfile_path(shell: str) -> Optional[Path]:
|
|
"""Get the rc file path for a shell."""
|
|
from os.path import expanduser
|
|
|
|
rcfiles = {
|
|
'bash': expanduser('~/.bashrc'),
|
|
'zsh': expanduser('~/.zshrc'),
|
|
'fish': expanduser('~/.config/fish/config.fish'),
|
|
}
|
|
|
|
path = rcfiles.get(shell)
|
|
return Path(path) if path else None
|
|
|
|
|
|
def _print_selected_aliases(suggestions: List):
|
|
"""Print selected aliases in a nice format."""
|
|
console.print("\n[bold green]Generated Aliases:[/bold green]\n")
|
|
|
|
for suggestion in suggestions:
|
|
console.print(f"[bold green]={suggestion.alias_name}[/bold green]='{suggestion.original_command}'")
|
|
|
|
console.print("\n[dim]Add these to your shell rc file or run with --export[/dim]")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|