diff --git a/src/cli.py b/src/cli.py new file mode 100644 index 0000000..a6fd4e2 --- /dev/null +++ b/src/cli.py @@ -0,0 +1,227 @@ +from typing import Optional +import os +from datetime import datetime + +import click +from rich.console import Console + +from src.git_insights import GitInsights +from src.utils.config import load_config + +console = Console() + + +@click.group() +@click.option( + "--repo-path", + "-p", + default=".", + help="Path to git repository", +) +@click.option( + "--days", + "-d", + default=30, + type=int, + help="Number of days to analyze", +) +@click.option( + "--config", + "-c", + default=None, + type=str, + help="Path to config file", +) +@click.pass_context +def main( + ctx: click.Context, + repo_path: str, + days: int, + config: Optional[str], +) -> None: + """Git Insights CLI - Analyze git repositories for productivity insights.""" + ctx.ensure_object(dict) + ctx.obj["repo_path"] = repo_path + ctx.obj["days"] = days + ctx.obj["config"] = config + + +@main.command() +@click.option( + "--format", + "-f", + type=click.Choice(["json", "markdown", "html", "console"]), + default="console", + help="Output format", +) +@click.pass_context +def analyze(ctx: click.Context, format: str) -> None: + """Analyze repository for commit patterns, code churn, and productivity metrics.""" + repo_path = ctx.obj["repo_path"] + days = ctx.obj["days"] + config_path = ctx.obj["config"] + + config = load_config(config_path) if config_path else None + + if not os.path.isdir(repo_path): + console.print(f"[red]Error: Directory not found: {repo_path}[/red]") + return + + try: + insights = GitInsights( + repo_path=repo_path, + days=days, + config=config, + ) + + results = insights.analyze() + + if format == "json": + from src.formatters import JSONFormatter + console.print(JSONFormatter.format(results)) + elif format == "markdown": + from src.formatters import MarkdownFormatter + console.print(MarkdownFormatter.format(results)) + elif format == "html": + from src.formatters import HTMLFormatter + console.print(HTMLFormatter.format(results)) + else: + from src.formatters import DashboardFormatter + DashboardFormatter.display(results) + + except Exception as e: + console.print(f"[red]Error: {e}[/red]") + raise + + +@main.command() +@click.pass_context +def dashboard(ctx: click.Context) -> None: + """Display productivity metrics in an interactive dashboard.""" + repo_path = ctx.obj["repo_path"] + days = ctx.obj["days"] + config_path = ctx.obj["config"] + + config = load_config(config_path) if config_path else None + + if not os.path.isdir(repo_path): + console.print(f"[red]Error: Directory not found: {repo_path}[/red]") + return + + try: + insights = GitInsights( + repo_path=repo_path, + days=days, + config=config, + ) + + results = insights.analyze() + from src.formatters import DashboardFormatter + DashboardFormatter.display(results) + + except Exception as e: + console.print(f"[red]Error: {e}[/red]") + raise + + +@main.command() +@click.option( + "--format", + "-f", + type=click.Choice(["json", "markdown", "html"]), + default="json", + help="Output format", +) +@click.option( + "--output", + "-o", + default=None, + type=str, + help="Output file path", +) +@click.pass_context +def export(ctx: click.Context, format: str, output: Optional[str]) -> None: + """Export analysis results to a file.""" + repo_path = ctx.obj["repo_path"] + days = ctx.obj["days"] + config_path = ctx.obj["config"] + + config = load_config(config_path) if config_path else None + + if not os.path.isdir(repo_path): + console.print(f"[red]Error: Directory not found: {repo_path}[/red]") + return + + try: + insights = GitInsights( + repo_path=repo_path, + days=days, + config=config, + ) + + results = insights.analyze() + + if format == "json": + from src.formatters import JSONFormatter + output_str = JSONFormatter.format(results) + elif format == "markdown": + from src.formatters import MarkdownFormatter + output_str = MarkdownFormatter.format(results) + else: + from src.formatters import HTMLFormatter + output_str = HTMLFormatter.format(results) + + if output: + with open(output, "w") as f: + f.write(output_str) + console.print(f"[green]Exported to {output}[/green]") + else: + console.print(output_str) + + except Exception as e: + console.print(f"[red]Error: {e}[/red]") + raise + + +@main.command() +@click.option( + "--output", + "-o", + default=None, + type=str, + help="Output file path", +) +@click.pass_context +def report(ctx: click.Context, output: Optional[str]) -> None: + """Generate a comprehensive productivity report.""" + repo_path = ctx.obj["repo_path"] + days = ctx.obj["days"] + config_path = ctx.obj["config"] + + config = load_config(config_path) if config_path else None + + if not os.path.isdir(repo_path): + console.print(f"[red]Error: Directory not found: {repo_path}[/red]") + return + + try: + insights = GitInsights( + repo_path=repo_path, + days=days, + config=config, + ) + + results = insights.analyze() + from src.formatters import HTMLFormatter + report_str = HTMLFormatter.format(results) + + if output: + with open(output, "w") as f: + f.write(report_str) + console.print(f"[green]Report generated: {output}[/green]") + else: + console.print(report_str) + + except Exception as e: + console.print(f"[red]Error: {e}[/red]") + raise