addOption('json', null, InputOption::VALUE_NONE, 'Render the full audit result as JSON.') ->addOption('details', null, InputOption::VALUE_NONE, 'Render detailed warning rows in the console summary.') ->addOption('all', null, InputOption::VALUE_NONE, 'Also include lower-priority REVIEW findings.'); } protected function execute(InputInterface $input, OutputInterface $output): int { $result = $this->provider->audit((bool) $input->getOption('all')); if ((bool) $input->getOption('json')) { $json = json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); $output->writeln(is_string($json) ? $json : '{}'); return Command::SUCCESS; } $this->renderSummary( new SymfonyStyle($input, $output), $result, (bool) $input->getOption('details'), (bool) $input->getOption('all') ); return Command::SUCCESS; } /** @param array $result */ private function renderSummary(SymfonyStyle $io, array $result, bool $details, bool $includeAll): void { $io->title('RetrieX core pattern audit'); $summary = is_array($result['summary'] ?? null) ? $result['summary'] : []; $io->definitionList( ['status' => (string) ($result['status'] ?? 'UNKNOWN')], ['source_files' => (string) ($summary['source_files'] ?? 0)], ['scanned_files' => (string) ($summary['scanned_files'] ?? 0)], ['skipped_files' => (string) ($summary['skipped_files'] ?? 0)], ['warning_findings' => (string) ($summary['warning_findings'] ?? 0)], ['review_findings' => (string) ($summary['review_findings'] ?? 0)], ['total_reported_findings' => (string) ($summary['total_reported_findings'] ?? 0)] ); $warnings = is_array($result['warnings'] ?? null) ? $result['warnings'] : []; if ($warnings !== []) { $io->section('Warnings'); foreach ($warnings as $warning) { $io->writeln('- ' . (string) $warning); } } if (!$details) { $note = 'Use --details for warning rows or --json for the complete machine-readable audit.'; if (!$includeAll) { $note .= ' Add --all to include lower-priority REVIEW findings.'; } $io->note($note); return; } $this->renderFindingTable($io, 'Warning findings', $result['warning_findings'] ?? []); if ($includeAll) { $this->renderFindingTable($io, 'Review findings', $result['review_findings'] ?? []); } } /** @param mixed $findings */ private function renderFindingTable(SymfonyStyle $io, string $title, mixed $findings): void { if (!is_array($findings) || $findings === []) { return; } $rows = []; foreach ($findings as $finding) { if (!is_array($finding)) { continue; } $rows[] = [ (string) ($finding['severity'] ?? ''), (string) ($finding['path'] ?? ''), (string) ($finding['line'] ?? ''), implode(', ', is_array($finding['calls'] ?? null) ? $finding['calls'] : []), implode(', ', is_array($finding['markers'] ?? null) ? $finding['markers'] : []), (string) ($finding['snippet'] ?? ''), ]; } if ($rows === []) { return; } $io->section($title); $io->table(['Severity', 'Path', 'Line', 'Calls', 'Markers', 'Snippet'], $rows); } }