diff --git a/lib/Enterprise/CliFramework.php b/lib/Enterprise/CliFramework.php index eca2c7c..425ad2e 100644 --- a/lib/Enterprise/CliFramework.php +++ b/lib/Enterprise/CliFramework.php @@ -590,6 +590,25 @@ abstract class CliFramework $this->display(' ' . $this->c($color . self::C_BOLD, $icon) . ' ' . $label . $suffix . "\n"); } + /** + * Display a recovery suggestion with a lightbulb prefix. + * + * @param string $suggestion Fix suggestion text (from RecoverySuggestion) + */ + protected function suggest(string $suggestion): void + { + if ($this->quiet) { + return; + } + $this->clearProgress(); + $lines = explode("\n", $suggestion); + $first = array_shift($lines); + $this->display(' ' . $this->c(self::C_YELLOW, '💡 ' . $first) . "\n"); + foreach ($lines as $line) { + $this->display(' ' . $this->c(self::C_YELLOW, ' ' . $line) . "\n"); + } + } + // ========================================================================= // Console graphics — progress bar // ========================================================================= diff --git a/lib/Enterprise/RecoverySuggestion.php b/lib/Enterprise/RecoverySuggestion.php new file mode 100644 index 0000000..87373fa --- /dev/null +++ b/lib/Enterprise/RecoverySuggestion.php @@ -0,0 +1,93 @@ +#!/usr/bin/env php + + * SPDX-License-Identifier: GPL-3.0-or-later + * FILE INFORMATION + * DEFGROUP: MokoPlatform.Enterprise + * INGROUP: MokoPlatform.Lib + * REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli + * PATH: /lib/Enterprise/RecoverySuggestion.php + * BRIEF: Smart error recovery suggestions for validators + */ + +declare(strict_types=1); + +namespace MokoCli; + +/** + * Generates actionable fix suggestions when validators detect problems. + * + * Each method returns a human-readable suggestion string that tells the + * developer exactly what to do to fix the issue. + */ +class RecoverySuggestion +{ + /** + * Suggest creating a missing required file. + */ + public static function forMissingFile(string $file, string $template = ''): string + { + $suggestion = "Create the missing file: {$file}"; + if ($template !== '') { + $suggestion .= "\n Copy from template: {$template}"; + } + return $suggestion; + } + + /** + * Suggest adding a missing XML element. + */ + public static function forMissingXmlElement(string $element, string $value, string $file, int $afterLine = 0): string + { + $snippet = "<{$element}>{$value}"; + if ($afterLine > 0) { + return "Add {$snippet} after line {$afterLine} in {$file}"; + } + return "Add {$snippet} to {$file}"; + } + + /** + * Suggest fixing a version mismatch. + */ + public static function forVersionMismatch(string $file, string $found, string $expected): string + { + return "Update version in {$file}: change \"{$found}\" to \"{$expected}\""; + } + + /** + * Suggest creating a missing directory. + */ + public static function forMissingDirectory(string $dir): string + { + return "Create the missing directory:\n mkdir -p {$dir}"; + } + + /** + * Suggest fixing a syntax error. + */ + public static function forSyntaxError(string $file, int $line, string $error): string + { + return "Fix syntax error at {$file}:{$line}\n {$error}"; + } + + /** + * Suggest fixing a missing license header. + */ + public static function forMissingHeader(string $file): string + { + return "Add SPDX license header to {$file}:\n /* Copyright (C) 2026 Moko Consulting \n * SPDX-License-Identifier: GPL-3.0-or-later */"; + } + + /** + * Suggest running a command to fix an issue. + */ + public static function forCommand(string $command, string $context = ''): string + { + $suggestion = "Run: {$command}"; + if ($context !== '') { + $suggestion = "{$context}\n {$suggestion}"; + } + return $suggestion; + } +} diff --git a/validate/check_structure.php b/validate/check_structure.php index b802615..196631e 100644 --- a/validate/check_structure.php +++ b/validate/check_structure.php @@ -20,6 +20,7 @@ declare(strict_types=1); require_once __DIR__ . '/../vendor/autoload.php'; use MokoCli\CliFramework; +use MokoCli\RecoverySuggestion; /** * Validates that the required directories and files exist in the repository root. @@ -67,6 +68,7 @@ class CheckStructure extends CliFramework if (!is_dir($path . '/' . $dir)) { $missingDirs[] = $dir; $this->status(false, "Directory: {$dir}"); + $this->suggest(RecoverySuggestion::forMissingDirectory($dir)); $failed++; } else { $this->status(true, "Directory: {$dir}"); @@ -96,6 +98,7 @@ class CheckStructure extends CliFramework if (!is_file($path . '/' . $file)) { $missingFiles[] = $file; $this->status(false, "File: {$file}"); + $this->suggest(RecoverySuggestion::forMissingFile($file)); $failed++; } else { $this->status(true, "File: {$file}"); diff --git a/validate/check_version_consistency.php b/validate/check_version_consistency.php index 510b1ec..78bbcb9 100755 --- a/validate/check_version_consistency.php +++ b/validate/check_version_consistency.php @@ -21,6 +21,7 @@ declare(strict_types=1); require_once __DIR__ . '/../vendor/autoload.php'; use MokoCli\CliFramework; +use MokoCli\RecoverySuggestion; /** * Checks that the version recorded in composer.json matches VERSION headers @@ -101,6 +102,7 @@ class CheckVersionConsistency extends CliFramework if ($match[0] !== $expected) { $line = substr_count(substr($content, 0, (int) $match[1]), "\n") + 1; $this->status(false, $filename, "line {$line}: found {$match[0]}, expected {$expected}"); + $this->suggest(RecoverySuggestion::forVersionMismatch($filename, $match[0], $expected)); $issues[] = $filename; $filePassed = false; }