Add CLI module with commands
Some checks failed
CI / test (push) Failing after 12s
CI / build (push) Has been skipped

This commit is contained in:
2026-02-01 08:27:03 +00:00
parent b2122d615c
commit 07d1051c68

View File

@@ -1,111 +1,113 @@
import sys
from datetime import datetime
from pathlib import Path
from typing import Optional from typing import Optional
import os
import click import click
from rich import print as rprint from rich.console import Console
from git_insights import GitInsights from src.git_insights import GitInsights
from git_insights.formatters import DashboardFormatter, JSONFormatter, MarkdownFormatter, HTMLFormatter from src.utils.config import load_config
console = Console()
@click.group() @click.group()
def main(): @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.pass_context
def main(ctx: click.Context, repo_path: str, days: int) -> None:
"""Git Insights CLI - Analyze git repositories for productivity insights.""" """Git Insights CLI - Analyze git repositories for productivity insights."""
pass ctx.ensure_object(dict)
ctx.obj["repo_path"] = repo_path
ctx.obj["days"] = days
@main.command() @main.command()
@click.argument("path", type=click.Path(exists=True), required=False, default=".") @click.option("--format", "-f", type=click.Choice(["json", "markdown", "html", "console"]), default="console")
@click.option("--days", type=int, default=30, help="Number of days to analyze") @click.pass_context
@click.option("--format", type=click.Choice(["json", "markdown", "html"]), default="console", help="Output format") def analyze(ctx: click.Context, format: str) -> None:
@click.option("-v", "--verbose", is_flag=True, help="Enable verbose output") """Analyze repository for commit patterns, code churn, and productivity metrics."""
def analyze(path: str, days: int, format: str, verbose: bool): repo_path = ctx.obj["repo_path"]
"""Analyze a git repository for commit patterns and productivity metrics.""" days = ctx.obj["days"]
if not os.path.isdir(repo_path):
console.print(f"[red]Error: Directory not found: {repo_path}[/red]")
return
try: try:
insights = GitInsights(path, days=days) insights = GitInsights(repo_path=repo_path, days=days)
result = insights.analyze() results = insights.analyze()
if format == "json": if format == "json":
click.echo(JSONFormatter.format(result)) from src.formatters import JSONFormatter
console.print(JSONFormatter.format(results))
elif format == "markdown": elif format == "markdown":
click.echo(MarkdownFormatter.format(result)) from src.formatters import MarkdownFormatter
console.print(MarkdownFormatter.format(results))
elif format == "html": elif format == "html":
click.echo(HTMLFormatter.format(result)) from src.formatters import HTMLFormatter
console.print(HTMLFormatter.format(results))
else: else:
DashboardFormatter.display(result) from src.formatters import DashboardFormatter
DashboardFormatter.display(results)
if verbose:
click.echo(f"\nAnalysis period: {days} days")
click.echo(f"Repository: {Path(path).resolve()}")
click.echo(f"Timestamp: {datetime.now().isoformat()}")
except Exception as e: except Exception as e:
rprint(f"[red]Error: {e}[/red]") console.print(f"[red]Error: {e}[/red]")
sys.exit(1) raise
@main.command() @main.command()
@click.argument("path", type=click.Path(exists=True), required=False, default=".") @click.pass_context
@click.option("--days", type=int, default=30, help="Number of days to analyze") def dashboard(ctx: click.Context) -> None:
def dashboard(path: str, days: int):
"""Display productivity metrics in an interactive dashboard.""" """Display productivity metrics in an interactive dashboard."""
repo_path = ctx.obj["repo_path"]
days = ctx.obj["days"]
if not os.path.isdir(repo_path):
console.print(f"[red]Error: Directory not found: {repo_path}[/red]")
return
try: try:
insights = GitInsights(path, days=days) insights = GitInsights(repo_path=repo_path, days=days)
result = insights.analyze() results = insights.analyze()
DashboardFormatter.display(result) from src.formatters import DashboardFormatter
DashboardFormatter.display(results)
except Exception as e: except Exception as e:
rprint(f"[red]Error: {e}[/red]") console.print(f"[red]Error: {e}[/red]")
sys.exit(1) raise
@main.command() @main.command()
@click.argument("path", type=click.Path(exists=True), required=False, default=".") @click.option("--format", "-f", type=click.Choice(["json", "markdown", "html"]), default="json")
@click.option("--days", type=int, default=30, help="Number of days to analyze") @click.option("--output", "-o", default=None, type=str, help="Output file path")
@click.option("--format", type=click.Choice(["json", "markdown", "html"]), default="json", help="Output format") @click.pass_context
@click.option("--output", type=click.Path(), help="Output file path") def export(ctx: click.Context, format: str, output: Optional[str]) -> None:
def export(path: str, days: int, format: str, output: Optional[str]):
"""Export analysis results to a file.""" """Export analysis results to a file."""
repo_path = ctx.obj["repo_path"]
days = ctx.obj["days"]
if not os.path.isdir(repo_path):
console.print(f"[red]Error: Directory not found: {repo_path}[/red]")
return
try: try:
insights = GitInsights(path, days=days) insights = GitInsights(repo_path=repo_path, days=days)
result = insights.analyze() results = insights.analyze()
if format == "json": if format == "json":
content = JSONFormatter.format(result) from src.formatters import JSONFormatter
output_str = JSONFormatter.format(results)
elif format == "markdown": elif format == "markdown":
content = MarkdownFormatter.format(result) from src.formatters import MarkdownFormatter
output_str = MarkdownFormatter.format(results)
else: else:
content = HTMLFormatter.format(result) from src.formatters import HTMLFormatter
output_str = HTMLFormatter.format(results)
if output: if output:
Path(output).write_text(content) with open(output, "w") as f:
click.echo(f"Exported to {output}") f.write(output_str)
console.print(f"[green]Exported to {output}[/green]")
else: else:
click.echo(content) console.print(output_str)
except Exception as e: except Exception as e:
rprint(f"[red]Error: {e}[/red]") console.print(f"[red]Error: {e}[/red]")
sys.exit(1) raise
@main.command()
@click.argument("path", type=click.Path(exists=True), required=False, default=".")
@click.option("--days", type=int, default=30, help="Number of days to analyze")
@click.option("--output", type=click.Path(), help="Output file path")
def report(path: str, days: int, output: Optional[str]):
"""Generate a comprehensive productivity report."""
try:
insights = GitInsights(path, days=days)
report = insights.get_productivity_report()
content = HTMLFormatter.format(report)
if output:
Path(output).write_text(content)
click.echo(f"Report generated: {output}")
else:
click.echo(content)
except Exception as e:
rprint(f"[red]Error: {e}[/red]")
sys.exit(1)