Initial upload of ai-code-audit-cli project
Some checks failed
Some checks failed
This commit is contained in:
123
src/cli/output.py
Normal file
123
src/cli/output.py
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
"""Output formatting for CLI results."""
|
||||||
|
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from rich.console import Console
|
||||||
|
from rich.panel import Panel
|
||||||
|
from rich.table import Table
|
||||||
|
from rich.text import Text
|
||||||
|
from rich.style import Style
|
||||||
|
from rich.color import Color
|
||||||
|
from rich.table import Column
|
||||||
|
|
||||||
|
from .options import OutputFormat, ScanOptions, SeverityLevel
|
||||||
|
from ..core.scanner import ScanResult, Issue, IssueCategory
|
||||||
|
|
||||||
|
|
||||||
|
class OutputFormatter:
|
||||||
|
"""Format and display scan results in terminal."""
|
||||||
|
|
||||||
|
def __init__(self, options: ScanOptions):
|
||||||
|
self.options = options
|
||||||
|
self.console = Console(no_color=options.no_color)
|
||||||
|
|
||||||
|
def display_results(
|
||||||
|
self, results: ScanResult, confidence_scorer: "ConfidenceScorer"
|
||||||
|
) -> None:
|
||||||
|
"""Display scan results to the console."""
|
||||||
|
score = confidence_scorer.calculate(results)
|
||||||
|
score_color = self._get_score_color(score)
|
||||||
|
|
||||||
|
self.console.print(Panel.fit(
|
||||||
|
f"[bold]AI Code Audit Results[/bold]\n\n"
|
||||||
|
f"Scanned: {results.files_scanned} files\n"
|
||||||
|
f"Issues Found: {len(results.issues)}\n"
|
||||||
|
f"[{score_color}]Confidence Score: {score}/100[/{score_color}]",
|
||||||
|
title="Audit Summary",
|
||||||
|
border_style="blue",
|
||||||
|
))
|
||||||
|
|
||||||
|
if self.options.verbose:
|
||||||
|
self._display_detailed_breakdown(results)
|
||||||
|
|
||||||
|
if results.issues:
|
||||||
|
self._display_issues(results.issues)
|
||||||
|
|
||||||
|
if results.warnings:
|
||||||
|
self._display_warnings(results.warnings)
|
||||||
|
|
||||||
|
def _get_score_color(self, score: int) -> str:
|
||||||
|
"""Get color style for score."""
|
||||||
|
if score >= 90:
|
||||||
|
return "green"
|
||||||
|
elif score >= 70:
|
||||||
|
return "yellow"
|
||||||
|
elif score >= 50:
|
||||||
|
return "orange1"
|
||||||
|
return "red"
|
||||||
|
|
||||||
|
def _display_detailed_breakdown(self, results: ScanResult) -> None:
|
||||||
|
"""Display detailed breakdown of scan results."""
|
||||||
|
table = Table(title="Detailed Breakdown", show_header=True)
|
||||||
|
table.add_column("Category", style="cyan")
|
||||||
|
table.add_column("Count", justify="right", style="magenta")
|
||||||
|
|
||||||
|
category_counts: dict[IssueCategory, int] = {}
|
||||||
|
for issue in results.issues:
|
||||||
|
category_counts[issue.category] = category_counts.get(issue.category, 0) + 1
|
||||||
|
|
||||||
|
for category, count in sorted(category_counts.items(), key=lambda x: -x[1]):
|
||||||
|
table.add_row(category.value, str(count))
|
||||||
|
|
||||||
|
severity_counts: dict[SeverityLevel, int] = {}
|
||||||
|
for issue in results.issues:
|
||||||
|
severity_counts[issue.severity] = severity_counts.get(issue.severity, 0) + 1
|
||||||
|
|
||||||
|
for severity in SeverityLevel:
|
||||||
|
count = severity_counts.get(severity, 0)
|
||||||
|
if count > 0:
|
||||||
|
table.add_row(f"{severity.value.capitalize()} Severity", str(count))
|
||||||
|
|
||||||
|
self.console.print(table)
|
||||||
|
|
||||||
|
def _display_issues(self, issues: list[Issue]) -> None:
|
||||||
|
"""Display issues in a formatted table."""
|
||||||
|
table = Table(
|
||||||
|
title="Issues Found",
|
||||||
|
show_header=True,
|
||||||
|
header_style="bold magenta",
|
||||||
|
)
|
||||||
|
table.add_column("Severity", width=10)
|
||||||
|
table.add_column("Category", width=15)
|
||||||
|
table.add_column("File", width=30)
|
||||||
|
table.add_column("Line", justify="right", width=6)
|
||||||
|
table.add_column("Message", width=50)
|
||||||
|
|
||||||
|
for issue in issues:
|
||||||
|
severity_style = self._get_severity_style(issue.severity)
|
||||||
|
table.add_row(
|
||||||
|
f"[{severity_style}]{issue.severity.value.capitalize()}[/{severity_style}]",
|
||||||
|
issue.category.value,
|
||||||
|
issue.file_path[:28] + ".." if len(issue.file_path) > 30 else issue.file_path,
|
||||||
|
str(issue.line_number),
|
||||||
|
issue.message[:48] + ".." if len(issue.message) > 50 else issue.message,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.console.print(table)
|
||||||
|
|
||||||
|
def _get_severity_style(self, severity: SeverityLevel) -> str:
|
||||||
|
"""Get Rich style for severity level."""
|
||||||
|
styles = {
|
||||||
|
SeverityLevel.CRITICAL: "red bold",
|
||||||
|
SeverityLevel.HIGH: "orange1",
|
||||||
|
SeverityLevel.MEDIUM: "yellow",
|
||||||
|
SeverityLevel.LOW: "blue",
|
||||||
|
}
|
||||||
|
return styles.get(severity, "white")
|
||||||
|
|
||||||
|
def _display_warnings(self, warnings: list[str]) -> None:
|
||||||
|
"""Display warnings."""
|
||||||
|
if warnings:
|
||||||
|
self.console.print("\n[yellow]Warnings:[/yellow]")
|
||||||
|
for warning in warnings:
|
||||||
|
self.console.print(f" - {warning}")
|
||||||
Reference in New Issue
Block a user