Initial upload of ai-code-audit-cli project
Some checks failed
CI / test (3.10) (push) Has been cancelled
CI / test (3.11) (push) Has been cancelled
CI / test (3.12) (push) Has been cancelled
CI / test (3.9) (push) Has been cancelled
CI / build (push) Has been cancelled
CI / release (push) Has been cancelled

This commit is contained in:
2026-02-03 10:29:59 +00:00
parent 65c3cccab8
commit 4e1d78c14c

123
src/cli/output.py Normal file
View 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}")