diff --git a/bin/moko b/bin/moko index ae03364..9aea292 100644 --- a/bin/moko +++ b/bin/moko @@ -241,24 +241,112 @@ function printCommandList(): void { echo "Available commands:\n\n"; - $groups = [ - 'Automation' => ['sync'], - 'Maintenance' => ['inventory'], - 'Validation (general)' => ['health', 'check:syntax', 'check:version', 'check:changelog', - 'check:structure', 'check:headers', 'check:secrets', - 'check:tabs', 'check:paths', 'check:xml', 'check:enterprise'], - 'Validation (platform)' => ['check:dolibarr', 'check:joomla', 'check:language', 'detect'], - 'Organisation-wide' => ['drift'], - ]; + // Auto-group by command prefix or comment-based sections + $groups = []; + foreach (COMMAND_MAP as $cmd => $path) { + if (str_contains($cmd, ':')) { + $prefix = explode(':', $cmd)[0]; + $groupName = match ($prefix) { + 'check' => 'Validation', + 'version' => 'Version', + 'release' => 'Release', + 'build' => 'Build', + 'platform', 'manifest' => 'Platform', + 'repo' => 'Repository', + 'bulk' => 'Bulk Operations', + 'client' => 'Client Management', + 'validate' => 'Module Validation', + default => ucfirst($prefix), + }; + } else { + $groupName = match ($cmd) { + 'sync' => 'Automation', + 'inventory' => 'Maintenance', + 'health' => 'Validation', + 'detect', 'drift' => 'Validation', + 'dashboard', 'grafana' => 'Monitoring', + default => 'Other', + }; + } + $groups[$groupName][$cmd] = $path; + } + + // Load plugin commands + $pluginCommands = loadPluginCommands(); + if (!empty($pluginCommands)) { + foreach ($pluginCommands as $cmd => $info) { + $type = $info['plugin'] ?? 'Plugin'; + $groups["Plugin: {$type}"][$cmd] = $info['description'] ?? ''; + } + } + + ksort($groups); foreach ($groups as $group => $commands) { - echo " {$group}:\n"; - foreach ($commands as $cmd) { - printf(" %-22s %s\n", $cmd, COMMAND_MAP[$cmd]); + echo " \033[1m{$group}\033[0m\n"; + ksort($commands); + foreach ($commands as $cmd => $path) { + printf(" \033[36m%-26s\033[0m %s\n", $cmd, basename($path)); } echo "\n"; } - echo "Run: php bin/moko --help for command-specific options.\n"; - echo "All platforms: php bin/moko \n"; + $total = count(COMMAND_MAP) + count($pluginCommands); + echo "{$total} command(s) available.\n"; + echo "Run: php bin/moko --help\n"; +} + +/** + * Load commands from registered plugins. + * + * @return array + */ +function loadPluginCommands(): array +{ + $pluginDir = dirname(__DIR__) . '/lib/Enterprise/Plugins'; + if (!is_dir($pluginDir)) { + return []; + } + + $commands = []; + + foreach (glob("{$pluginDir}/*Plugin.php") as $file) { + $className = 'MokoEnterprise\\Plugins\\' + . pathinfo($file, PATHINFO_FILENAME); + + if (!class_exists($className)) { + continue; + } + + try { + $ref = new \ReflectionClass($className); + if ($ref->isAbstract()) { + continue; + } + + $plugin = $ref->newInstanceWithoutConstructor(); + $pluginCmds = $plugin->getCommands(); + + foreach ($pluginCmds as $cmd) { + $name = $cmd['name'] ?? ''; + if ($name === '') { + continue; + } + + $type = method_exists($plugin, 'getProjectType') + ? $plugin->getProjectType() : 'unknown'; + + $commands[$name] = [ + 'plugin' => $type, + 'description' => $cmd['description'] ?? '', + 'script' => $cmd['script'] ?? '', + ]; + } + } catch (\Throwable $e) { + // Skip plugins that can't be instantiated + continue; + } + } + + return $commands; }