126 lines
4.4 KiB
PHP
126 lines
4.4 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Command;
|
|
|
|
use App\Config\CorePatternAuditProvider;
|
|
use Symfony\Component\Console\Attribute\AsCommand;
|
|
use Symfony\Component\Console\Command\Command;
|
|
use Symfony\Component\Console\Input\InputInterface;
|
|
use Symfony\Component\Console\Input\InputOption;
|
|
use Symfony\Component\Console\Output\OutputInterface;
|
|
use Symfony\Component\Console\Style\SymfonyStyle;
|
|
|
|
#[AsCommand(
|
|
name: 'mto:agent:config:audit-patterns',
|
|
description: 'Audit remaining core pattern and token usage for developer-policy review'
|
|
)]
|
|
final class ConfigPatternAuditCommand extends Command
|
|
{
|
|
public function __construct(private readonly CorePatternAuditProvider $provider)
|
|
{
|
|
parent::__construct();
|
|
}
|
|
|
|
protected function configure(): void
|
|
{
|
|
$this
|
|
->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<string, mixed> $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);
|
|
}
|
|
}
|