This commit is contained in:
151
shellhist/cli/export.py
Normal file
151
shellhist/cli/export.py
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
"""Export command for generating scripts from detected patterns."""
|
||||||
|
|
||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
import click
|
||||||
|
from rich.console import Console
|
||||||
|
|
||||||
|
from shellhist.core import HistoryLoader
|
||||||
|
from shellhist.core.export import generate_script, generate_script_name
|
||||||
|
from shellhist.core.patterns import detect_common_sequences
|
||||||
|
|
||||||
|
|
||||||
|
@click.command("export-script")
|
||||||
|
@click.option(
|
||||||
|
"--history",
|
||||||
|
"-H",
|
||||||
|
type=str,
|
||||||
|
help="Path to history file",
|
||||||
|
)
|
||||||
|
@click.option(
|
||||||
|
"--output",
|
||||||
|
"-o",
|
||||||
|
type=str,
|
||||||
|
default=".",
|
||||||
|
help="Output directory for generated scripts (default: current directory)",
|
||||||
|
)
|
||||||
|
@click.option(
|
||||||
|
"--name",
|
||||||
|
"-n",
|
||||||
|
type=str,
|
||||||
|
help="Custom name for the script file",
|
||||||
|
)
|
||||||
|
@click.option(
|
||||||
|
"--dry-run",
|
||||||
|
"-d",
|
||||||
|
is_flag=True,
|
||||||
|
default=False,
|
||||||
|
help="Preview script without writing to disk",
|
||||||
|
)
|
||||||
|
@click.option(
|
||||||
|
"--force",
|
||||||
|
"-f",
|
||||||
|
is_flag=True,
|
||||||
|
default=False,
|
||||||
|
help="Overwrite existing scripts",
|
||||||
|
)
|
||||||
|
@click.option(
|
||||||
|
"--shell",
|
||||||
|
"-s",
|
||||||
|
type=click.Choice(["bash", "zsh"]),
|
||||||
|
help="Shell type for parsing",
|
||||||
|
)
|
||||||
|
@click.option(
|
||||||
|
"--min-occurrences",
|
||||||
|
"-m",
|
||||||
|
type=int,
|
||||||
|
default=3,
|
||||||
|
help="Minimum occurrences for pattern (default: 3)",
|
||||||
|
)
|
||||||
|
@click.pass_context
|
||||||
|
def export_script_command(
|
||||||
|
ctx: click.Context,
|
||||||
|
history: Optional[str],
|
||||||
|
output: str,
|
||||||
|
name: Optional[str],
|
||||||
|
dry_run: bool,
|
||||||
|
force: bool,
|
||||||
|
shell: Optional[str],
|
||||||
|
min_occurrences: int,
|
||||||
|
) -> None:
|
||||||
|
"""Export detected command patterns to executable shell scripts.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
\b
|
||||||
|
shellhist export-script --output ./scripts/
|
||||||
|
shellhist export-script --output ./scripts/ --name myroutine
|
||||||
|
shellhist export-script --dry-run --min-occurrences 5
|
||||||
|
"""
|
||||||
|
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
|
||||||
|
|
||||||
|
sequences = detect_common_sequences(
|
||||||
|
store,
|
||||||
|
max_length=5,
|
||||||
|
min_occurrences=min_occurrences,
|
||||||
|
)
|
||||||
|
|
||||||
|
if not sequences:
|
||||||
|
console.print(
|
||||||
|
f"[yellow]No significant patterns found with minimum {min_occurrences} occurrences.[/yellow]"
|
||||||
|
)
|
||||||
|
console.print("Try lowering --min-occurrences or run more commands.")
|
||||||
|
return
|
||||||
|
|
||||||
|
console.print(f"[bold cyan]Detected Patterns for Script Export[/bold cyan]\n")
|
||||||
|
|
||||||
|
scripts_generated = 0
|
||||||
|
|
||||||
|
for i, pattern in enumerate(sequences[:5], 1):
|
||||||
|
script_name = name or generate_script_name(pattern)
|
||||||
|
|
||||||
|
console.print(f"{i}. Pattern: {' -> '.join(pattern.commands)}")
|
||||||
|
console.print(f" Script name: {script_name}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
script_path, content = generate_script(
|
||||||
|
pattern=pattern,
|
||||||
|
script_name=script_name,
|
||||||
|
output_dir=output,
|
||||||
|
dry_run=dry_run,
|
||||||
|
force=force,
|
||||||
|
)
|
||||||
|
|
||||||
|
if dry_run:
|
||||||
|
console.print(f" [yellow](dry-run) Would create: {script_path}[/yellow]")
|
||||||
|
console.print(f" Content preview:")
|
||||||
|
for line in content.split("\n")[:5]:
|
||||||
|
console.print(f" {line}")
|
||||||
|
console.print()
|
||||||
|
else:
|
||||||
|
console.print(f" [green]Created: {script_path}[/green]\n")
|
||||||
|
scripts_generated += 1
|
||||||
|
|
||||||
|
except FileExistsError:
|
||||||
|
console.print(f" [yellow]Skipped: {script_name}.sh (already exists, use --force to overwrite)[/yellow]\n")
|
||||||
|
except Exception as e:
|
||||||
|
console.print(f" [red]Error: {e}[/red]\n")
|
||||||
|
|
||||||
|
if not dry_run and scripts_generated > 0:
|
||||||
|
console.print(
|
||||||
|
f"[green]Successfully generated {scripts_generated} script(s) in {output}[/green]"
|
||||||
|
)
|
||||||
|
elif dry_run:
|
||||||
|
console.print("[yellow]Scripts not created (dry-run mode)[/yellow]")
|
||||||
|
|
||||||
|
except FileNotFoundError as e:
|
||||||
|
console.print(f"[red]Error: {e}[/red]")
|
||||||
|
ctx.exit(1)
|
||||||
|
except Exception as e:
|
||||||
|
console.print(f"[red]Error exporting scripts: {e}[/red]")
|
||||||
|
ctx.exit(1)
|
||||||
Reference in New Issue
Block a user