Merge pull request 'feat: plugin command dispatcher + auto-grouped list' (#115) from dev into main
Platform: moko-platform CI / CI Summary (push) Blocked by required conditions
Universal: Cascade Main → Dev / Cascade main → branches (push) Successful in 5s
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 3s
Platform: moko-platform CI / Gate 1: Code Quality (push) Successful in 1m16s
Generic: Repo Health / Release configuration (push) Successful in 8s
Generic: Repo Health / Scripts governance (push) Successful in 7s
Generic: Repo Health / Repository health (push) Successful in 16s
Platform: moko-platform CI / Gate 5: Template Integrity (push) Failing after 13s
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Successful in 1m5s
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Successful in 1m7s
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Failing after 1m5s
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Successful in 1m11s
Platform: moko-platform CI / Gate 4: Governance (push) Successful in 1m8s

This commit was merged in pull request #115.
This commit is contained in:
2026-05-26 02:48:16 +00:00
+102 -14
View File
@@ -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 <command> --help for command-specific options.\n";
echo "All platforms: php bin/moko <command>\n";
$total = count(COMMAND_MAP) + count($pluginCommands);
echo "{$total} command(s) available.\n";
echo "Run: php bin/moko <command> --help\n";
}
/**
* Load commands from registered plugins.
*
* @return array<string, array{plugin: string, description: string, script: string}>
*/
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;
}