success("No violations found!"); if ($baselinedCount > 0) { $io->comment("($baselinedCount violations ignored by baseline)"); } return Command::SUCCESS; } // Group by file $grouped = []; foreach ($violations as $v) { $file = $v->location['file'] ?? 'unknown'; $grouped[$file][] = $v; } // Sort files alphabetically for stable order ksort($grouped); foreach ($grouped as $file => $fileViolations) { $io->section("File: " . $file); // Sort violations by line number usort($fileViolations, function ($a, $b) { return ($a->location['line'] ?? 0) <=> ($b->location['line'] ?? 0); }); foreach ($fileViolations as $v) { $line = $v->location['line'] ?? 0; $severityColor = 'red'; if ($v->severity->value === 'warning') { $severityColor = 'yellow'; } elseif ($v->severity->value === 'info') { $severityColor = 'cyan'; } // Format the output $io->writeln(sprintf( " Line %d: [%s] %s", $line, $severityColor, $v->ruleId, $v->message )); // Show snippet $snippet = (string)preg_replace('/\s+/', ' ', trim($v->htmlSnippet)); $io->writeln(" Snippet: " . htmlspecialchars($snippet) . ""); if ($v->fixSuggestion !== null && $v->fixSuggestion !== '') { $io->writeln(" AI Suggestion: " . htmlspecialchars(trim($v->fixSuggestion)) . ""); } $io->writeln(""); } } $totalViolations = count($violations); $io->error(sprintf( "Found %d %s across %d %s.", $totalViolations, $totalViolations === 1 ? 'violation' : 'violations', count($grouped), count($grouped) === 1 ? 'file' : 'files' )); if ($baselinedCount > 0) { $io->comment("($baselinedCount violations ignored by baseline)"); } return Command::FAILURE; } }