Source code for super_pocket.project.init.cli

"""
CLI commands for project initialization.

Provides Click commands for listing, showing, and initializing projects
from templates.
"""
from super_pocket.settings import (
    click,
    CONTEXT_SETTINGS,
    add_help_command,
    centered_spinner,
    display_logo
)
from pathlib import Path
from rich.console import Console
from rich.table import Table
from rich.live import Live
from super_pocket.utils import print_error
from .manifest import parse_manifest
from .engine import ProjectGenerator
from .interactive import customize_interactively
from .validation import (
    validate_project_name,
    validate_output_path,
    validate_template_name,
    ValidationError
)


console = Console()


[docs] def list_templates(templates_dir: Path) -> list[dict]: """ List all available templates. Args: templates_dir: Directory containing template manifests Returns: List of template info dicts """ display_logo() with Live(centered_spinner("Loading templates..."), refresh_per_second=20, transient=True): templates = [] for manifest_file in templates_dir.glob("*.yaml"): try: manifest = parse_manifest(manifest_file) templates.append({ "name": manifest.name, "display_name": manifest.display_name, "description": manifest.description }) except Exception as e: print_error(e, custom=True, message=f"Error loading {manifest_file.name}") display_logo() return templates
@click.group(name="init", context_settings=CONTEXT_SETTINGS) def init_group(): """Initialize new projects from templates.""" pass @init_group.command(name="list", context_settings=CONTEXT_SETTINGS) def list_cmd(): """List available project templates.""" templates_dir = Path(__file__).parent.parent / "templates" templates = list_templates(templates_dir) if not templates: display_logo() console.print("[yellow]No templates found[/yellow]", justify="center") return table = Table(title="Available Templates") table.add_column("Name", style="cyan") table.add_column("Display Name", style="green") table.add_column("Description") for template in templates: table.add_row( template["name"], template["display_name"], template["description"] ) console.print(table) @init_group.command(name="show", context_settings=CONTEXT_SETTINGS) @click.argument("template_name") def show_cmd(template_name: str): """Show details of a specific template.""" templates_dir = Path(__file__).parent.parent / "templates" manifest_path = templates_dir / f"{template_name}.yaml" if not manifest_path.exists(): console.print(f"[red]Template not found: {template_name}[/red]") return try: manifest = parse_manifest(manifest_path) console.print(f"\n[bold cyan]{manifest.display_name}[/bold cyan]") console.print(f"{manifest.description}\n") console.print(f"Python version: {manifest.python_version}") if manifest.tool_choices: console.print("\n[bold]Tool Choices:[/bold]") for key, choice in manifest.tool_choices.items(): console.print(f" {choice.prompt}") for option in choice.options: default = " (default)" if option.name == choice.default else "" console.print(f" - {option.name}: {option.description}{default}") if manifest.features: console.print("\n[bold]Features:[/bold]") for feature in manifest.features: default = "✓" if feature.default else "✗" console.print(f" [{default}] {feature.description}") except Exception as e: print_error(e, custom=True, message="Error loading template") raise @init_group.command(context_settings=CONTEXT_SETTINGS) @click.argument("template_name") @click.option("--path", "-p", type=click.Path(), help="Output directory") @click.option("--quick", "-q", is_flag=True, help="Use defaults without prompting") def new(template_name: str, path: str | None, quick: bool): """ Initialize a new project from a template. Args: template_name: Name of the template to use path: Output directory (defaults to ./<project_name>) quick: Use default selections without prompting """ templates_dir = Path(__file__).parent.parent / "templates" manifest_path = templates_dir / f"{template_name}.yaml" try: # Validate template name available_templates = list_templates(templates_dir) available_names = [t['name'] for t in available_templates] validate_template_name(template_name, available_names) # Load manifest manifest = parse_manifest(manifest_path) # Get user customization project_name, description, tool_sel, feat_sel = customize_interactively( manifest, quick=quick ) # Validate project name try: validate_project_name(project_name) except ValidationError as e: print_error(e, custom=True, message="Invalid project name") raise click.Abort() # Determine output path if path: output_path = Path(path) else: output_path = Path.cwd() / project_name # Validate output path try: validate_output_path(output_path, allow_existing=False) except ValidationError as e: print_error(e, custom=True, message="Invalid output path") raise click.Abort() # Generate project with Live(centered_spinner("Generating project..."), refresh_per_second=20, transient=True): generator = ProjectGenerator( manifest=manifest, project_name=project_name, output_path=output_path ) generator.set_selections(tool_sel, feat_sel, description) results = generator.generate() # Display results success_count = sum(1 for r in results if r.success) console.print(f"\n[green]✓ Generated {success_count} items successfully[/green]") # Show any errors errors = [r for r in results if not r.success] if errors: console.print(f"\n[yellow]Warnings/Errors:[/yellow]") for error in errors: console.print(f" [red]✗[/red] {error.message}") if error.error: console.print(f" {error.error}") console.print(f"\n[bold green]Project created successfully![/bold green]") console.print(f"Location: {output_path}") except ValidationError as e: print_error(e, custom=True, message="Validation Error") raise except KeyboardInterrupt: console.print("\n[yellow]Cancelled[/yellow]") except Exception as e: print_error(e, custom=True, message="Error") raise # Add 'help' subcommand to the group add_help_command(init_group)