Git Komut Rehberi & Terminal Asistanı
Sıfırdan Yeni Bir Depo Başlatın:
Mevcut Bir Git Deposu veya Klasörü Gönderin:
Hataları Geri Alma, İptal & Temizlik:
Son commit'i geri al (yapılan değişiklikleri korur):
git reset --soft HEAD~1
Yapılan yerel değişiklikleri iptal et (çalışma ağacını temizler):
git restore .
Takip edilmeyen gereksiz dosyaları sil:
git clean -fd
Değişiklikleri geçici sakla ve geri getir (stash):
git stash git stash pop
Dal (Branch) ve Sürüm Etiketleri:
Yeni dal aç ve geçiş yap:
git checkout -b yeni-ozellik git push -u origin yeni-ozellik
Sürüm etiketi (tag) oluştur ve gönder:
git tag -a v1.0.0 -m "Sürüm 1.0.0" git push origin --tags
136 lines
5.0 KiB
PHP
136 lines
5.0 KiB
PHP
<?php
|
|||
|
|
|
||
|
|
namespace YakNet\AccessibilityConsole\Console\Command;
|
||
|
|
|
||
|
|
use Symfony\Component\Console\Command\Command;
|
||
|
|
use Symfony\Component\Console\Input\InputArgument;
|
||
|
|
use Symfony\Component\Console\Input\InputInterface;
|
||
|
|
use Symfony\Component\Console\Input\InputOption;
|
||
|
|
use Symfony\Component\Console\Output\OutputInterface;
|
||
|
|
use Symfony\Component\Console\Style\SymfonyStyle;
|
||
|
|
use YakNet\AccessibilityConsole\AI\GeminiFixer;
|
||
|
|
use YakNet\AccessibilityConsole\Core\Config;
|
||
|
|
use YakNet\AccessibilityConsole\Core\Scanner;
|
||
|
|
use YakNet\AccessibilityConsole\Rules\StandardRuleSet;
|
||
|
|
use YakNet\AccessibilityConsole\Source\SourceLocator;
|
||
|
|
|
||
|
|
class FixCommand extends Command
|
||
|
|
{
|
||
|
|
protected function configure(): void
|
||
|
|
{
|
||
|
|
$this
|
||
|
|
->setName('fix')
|
||
|
|
->setDescription('Automatically fix accessibility violations using AI')
|
||
|
|
->addArgument('target', InputArgument::REQUIRED, 'The URL or file path to scan')
|
||
|
|
->addOption('project-path', null, InputOption::VALUE_REQUIRED, 'Base path for source mapping')
|
||
|
|
->addOption('dry-run', null, InputOption::VALUE_NONE, 'Show what would be fixed without modifying files');
|
||
|
|
}
|
||
|
|
|
||
|
|
protected function execute(InputInterface $input, OutputInterface $output): int
|
||
|
|
{
|
||
|
|
$io = new SymfonyStyle($input, $output);
|
||
|
|
$target = $input->getArgument('target');
|
||
|
|
$projectPath = $input->getOption('project-path') ?? getcwd();
|
||
|
|
$dryRun = $input->getOption('dry-run');
|
||
|
|
|
||
|
|
$config = new Config();
|
||
|
|
$apiKey = $config->getGeminiApiKey();
|
||
|
|
|
||
|
|
if (!$apiKey) {
|
||
|
|
$io->error("GEMINI_API_KEY not found in .env or environment variables. AI fixing requires an API key.");
|
||
|
|
return Command::FAILURE;
|
||
|
|
}
|
||
|
|
|
||
|
|
$io->title("AI Self-Healing Mode - Fixing: $target");
|
||
|
|
|
||
|
|
// 1. Fetch HTML
|
||
|
|
$html = @file_get_contents($target);
|
||
|
|
if (!$html) {
|
||
|
|
$io->error("Could not read target: $target");
|
||
|
|
return Command::FAILURE;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 2. Scan
|
||
|
|
$scanner = new Scanner();
|
||
|
|
foreach (StandardRuleSet::all() as $rule) {
|
||
|
|
$scanner->addRule($rule);
|
||
|
|
}
|
||
|
|
$violations = $scanner->scan($html);
|
||
|
|
|
||
|
|
if (empty($violations)) {
|
||
|
|
$io->success("Everything looks good! No violations to fix.");
|
||
|
|
return Command::SUCCESS;
|
||
|
|
}
|
||
|
|
|
||
|
|
$io->note("Found " . count($violations) . " violations. Starting AI analysis...");
|
||
|
|
|
||
|
|
$fixer = new GeminiFixer($apiKey);
|
||
|
|
$locator = new SourceLocator($projectPath);
|
||
|
|
$fixedCount = 0;
|
||
|
|
|
||
|
|
foreach ($violations as $violation) {
|
||
|
|
$io->write("Analyzing <fg=yellow>{$violation->ruleId}</>... ");
|
||
|
|
|
||
|
|
// Locate source
|
||
|
|
$location = $locator->locate($violation->htmlSnippet);
|
||
|
|
if (!$location) {
|
||
|
|
$io->writeln("<fg=red>Skipped (Source not found)</>");
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Get AI Suggestion
|
||
|
|
$suggestion = $fixer->suggestFix($violation);
|
||
|
|
if (!$suggestion) {
|
||
|
|
$io->writeln("<fg=red>Skipped (AI could not generate fix)</>");
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
$io->writeln("<fg=green>Fix Found!</>");
|
||
|
|
$io->writeln(" <fg=gray>Original:</> <fg=red>" . htmlspecialchars(trim($violation->htmlSnippet)) . "</>");
|
||
|
|
$io->writeln(" <fg=gray>AI Fix: </> <fg=green>" . htmlspecialchars(trim($suggestion)) . "</>");
|
||
|
|
|
||
|
|
if ($dryRun) {
|
||
|
|
$io->writeln(" <fg=blue>[DRY RUN] File modification skipped.</>");
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Apply Fix
|
||
|
|
if ($this->applyFix($location['file'], $violation->htmlSnippet, $suggestion)) {
|
||
|
|
$io->writeln(" <fg=bright-green>✔ Applied to {$location['file']}:{$location['line']}</>");
|
||
|
|
$fixedCount++;
|
||
|
|
} else {
|
||
|
|
$io->writeln(" <fg=red>✘ Failed to apply fix to file.</>");
|
||
|
|
}
|
||
|
|
$io->writeln("");
|
||
|
|
}
|
||
|
|
|
||
|
|
if ($fixedCount > 0) {
|
||
|
|
$io->success("Self-healing complete! $fixedCount violations were automatically fixed.");
|
||
|
|
} else {
|
||
|
|
$io->warning("No files were modified. Check error logs for details.");
|
||
|
|
}
|
||
|
|
|
||
|
|
return Command::SUCCESS;
|
||
|
|
}
|
||
|
|
|
||
|
|
private function applyFix(string $filePath, string $originalSnippet, string $fixedSnippet): bool
|
||
|
|
{
|
||
|
|
$content = file_get_contents($filePath);
|
||
|
|
if ($content === false) return false;
|
||
|
|
|
||
|
|
// Try exact replacement first
|
||
|
|
if (str_contains($content, $originalSnippet)) {
|
||
|
|
$newContent = str_replace($originalSnippet, $fixedSnippet, $content);
|
||
|
|
} else {
|
||
|
|
// Fallback: Fuzzy replacement (simplified for this demo)
|
||
|
|
// In a real professional lib, we'd use more advanced regex or DOM patching
|
||
|
|
$newContent = str_replace(trim($originalSnippet), trim($fixedSnippet), $content);
|
||
|
|
}
|
||
|
|
|
||
|
|
if ($newContent === $content) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
return file_put_contents($filePath, $newContent) !== false;
|
||
|
|
}
|
||
|
|
}
|