` namespace handler. */ class TMDO_CLI_HivePress { /** * List installed HivePress family addons + adapter binding state. * * ## OPTIONS * * [--format=] * : Output format (table|json|csv|yaml). Default: table. * * ## EXAMPLES * * wp wpdo hivepress detect * wp wpdo hivepress detect --format=json * * @param array $args Positional args (unused). * @param array $assoc_args Flag args. * * @subcommand detect */ public function detect( $args, $assoc_args ): void { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed unset( $args ); $format = (string) ( $assoc_args['format'] ?? 'table' ); $detected = class_exists( 'TMDO_HivePress_Detector' ) ? TMDO_HivePress_Detector::detect() : array(); $catalog = class_exists( 'TMDO_HivePress_Detector' ) ? TMDO_HivePress_Detector::catalog() : array(); $bound = class_exists( 'TMDO_HivePress_Bootstrap' ) ? array_keys( TMDO_HivePress_Bootstrap::adapters() ) : array(); $rows = array(); foreach ( $catalog as $slug => $name ) { $rows[] = array( 'slug' => $slug, 'name' => $name, 'detected' => isset( $detected[ $slug ] ) ? 'yes' : 'no', 'version' => $detected[ $slug ] ?? '-', 'bound' => in_array( $slug, $bound, true ) ? 'yes' : 'no', ); } if ( ! class_exists( 'WP_CLI\\Utils' ) || 'table' === $format ) { WP_CLI::log( '=== HivePress family detection ===' ); WP_CLI::log( '' ); WP_CLI::log( str_pad( 'slug', 26 ) . str_pad( 'detected', 12 ) . str_pad( 'version', 10 ) . 'bound' ); WP_CLI::log( str_repeat( '-', 60 ) ); foreach ( $rows as $row ) { WP_CLI::log( str_pad( $row['slug'], 26 ) . str_pad( $row['detected'], 12 ) . str_pad( $row['version'], 10 ) . $row['bound'] ); } WP_CLI::log( '' ); WP_CLI::success( sprintf( 'Detected %d / %d addons', count( $detected ), count( $catalog ) ) ); return; } \WP_CLI\Utils\format_items( $format, $rows, array( 'slug', 'name', 'detected', 'version', 'bound' ) ); } /** * Show 8-dimension suitability report (per-adapter + aggregate). * * ## OPTIONS * * [--addon=] * : Filter to a single addon slug. * * [--format=] * : Output format (table|json|yaml). Default: table. * * ## EXAMPLES * * wp wpdo hivepress score * wp wpdo hivepress score --addon=hivepress-reviews * wp wpdo hivepress score --format=json * * @param array $args Positional args (unused). * @param array $assoc_args Flag args. * * @subcommand score */ public function score( $args, $assoc_args ): void { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed unset( $args ); if ( ! class_exists( 'TMDO_HivePress_Suitability_Scorer' ) ) { WP_CLI::error( 'TMDO_HivePress_Suitability_Scorer not loaded.' ); } $report = TMDO_HivePress_Suitability_Scorer::report(); $addon = isset( $assoc_args['addon'] ) ? (string) $assoc_args['addon'] : ''; $format = (string) ( $assoc_args['format'] ?? 'table' ); if ( '' !== $addon ) { if ( ! isset( $report['adapters'][ $addon ] ) ) { WP_CLI::error( sprintf( 'Addon "%s" not active.', $addon ) ); } $score = $report['adapters'][ $addon ]; if ( 'json' === $format || 'yaml' === $format ) { \WP_CLI\Utils\format_items( $format, array( $score ), array_keys( $score ) ); return; } WP_CLI::log( sprintf( '=== Score: %s ===', $addon ) ); foreach ( $score as $k => $v ) { WP_CLI::log( sprintf( ' %s: %s', str_pad( $k, 22 ), $v ) ); } return; } if ( 'json' === $format || 'yaml' === $format ) { \WP_CLI\Utils\format_items( $format, array( $report ), array( 'aggregate', 'adapter_count', 'per_dimension', 'adapters' ) ); return; } WP_CLI::log( '=== HivePress 8-D Suitability Report ===' ); WP_CLI::log( '' ); WP_CLI::log( sprintf( 'Active adapters: %d', $report['adapter_count'] ) ); WP_CLI::log( sprintf( 'Aggregate score: %s / 10', $report['aggregate'] ) ); WP_CLI::log( '' ); WP_CLI::log( 'Per-dimension averages:' ); foreach ( $report['per_dimension'] as $dim => $avg ) { WP_CLI::log( sprintf( ' %s: %s', str_pad( $dim, 22 ), $avg ) ); } WP_CLI::log( '' ); WP_CLI::log( 'Per-adapter aggregate:' ); foreach ( $report['adapters'] as $slug => $score ) { WP_CLI::log( sprintf( ' %s → %s', str_pad( $slug, 26 ), $score['aggregate'] ?? '-' ) ); } } /** * Run doctor probe for each active adapter. * * ## OPTIONS * * [--addon=] * : Limit probe to a single addon. * * ## EXAMPLES * * wp wpdo hivepress doctor * wp wpdo hivepress doctor --addon=hivepress-favorites * * @param array $args Positional args (unused). * @param array $assoc_args Flag args. * * @subcommand doctor */ public function doctor( $args, $assoc_args ): void { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed unset( $args ); $adapters = class_exists( 'TMDO_HivePress_Bootstrap' ) ? TMDO_HivePress_Bootstrap::adapters() : array(); $only = isset( $assoc_args['addon'] ) ? (string) $assoc_args['addon'] : ''; $failed = 0; $total = 0; foreach ( $adapters as $slug => $adapter ) { if ( '' !== $only && $slug !== $only ) { continue; } if ( ! $adapter instanceof TMDO_HivePress_Adapter ) { continue; } ++$total; $probe = $adapter->doctor_check(); if ( ! empty( $probe['ok'] ) ) { WP_CLI::log( sprintf( '✓ %s: %s', $slug, $probe['message'] ?? 'OK' ) ); } else { WP_CLI::log( sprintf( '✗ %s: %s', $slug, $probe['message'] ?? 'FAIL' ) ); ++$failed; } } if ( 0 === $total ) { WP_CLI::warning( 'No adapters active. Install HivePress first.' ); return; } if ( $failed > 0 ) { WP_CLI::error( sprintf( 'Doctor: %d / %d adapters failed.', $failed, $total ) ); } WP_CLI::success( sprintf( 'Doctor: %d / %d adapters healthy.', $total, $total ) ); } /** * Flip an addon's modules to a target FSM state (default: cutover). * * ## OPTIONS * * * : Addon slug (e.g. hivepress-reviews). * * [--state=] * : Target state. One of: dual_write, backfill, verify, cutover, cleanup, complete. Default: cutover. * * ## EXAMPLES * * wp wpdo hivepress migrate hivepress-reviews * wp wpdo hivepress migrate hivepress-favorites --state=dual_write * * @param array $args Positional args (slug). * @param array $assoc_args Flag args. * * @subcommand migrate */ public function migrate( $args, $assoc_args ): void { $slug = (string) ( $args[0] ?? '' ); $state = (string) ( $assoc_args['state'] ?? 'cutover' ); $this->set_state( $slug, $state ); } /** * Roll an addon's modules back to idle. * * ## OPTIONS * * * : Addon slug. * * ## EXAMPLES * * wp wpdo hivepress rollback hivepress-reviews * * @param array $args Positional args (slug). * @param array $assoc_args Flag args (unused). * * @subcommand rollback */ public function rollback( $args, $assoc_args ): void { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed unset( $assoc_args ); $slug = (string) ( $args[0] ?? '' ); $this->set_state( $slug, 'idle' ); } /** * Run benchmark suite for active adapters. * * Currently a placeholder that reports availability — real query-pair * benchmarks are wired by individual addons during their migration * tooling. This command lists which adapters can be benchmarked. * * ## EXAMPLES * * wp wpdo hivepress benchmark * * @param array $args Positional args (unused). * @param array $assoc_args Flag args (unused). * * @subcommand benchmark */ public function benchmark( $args, $assoc_args ): void { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed unset( $args, $assoc_args ); if ( ! class_exists( 'TMDO_HivePress_Benchmark' ) ) { WP_CLI::error( 'TMDO_HivePress_Benchmark not loaded.' ); } $adapters = class_exists( 'TMDO_HivePress_Bootstrap' ) ? TMDO_HivePress_Bootstrap::adapters() : array(); if ( empty( $adapters ) ) { WP_CLI::warning( 'No adapters active — no benchmark to run.' ); return; } WP_CLI::log( '=== HivePress benchmark availability ===' ); foreach ( $adapters as $slug => $adapter ) { $mods = $adapter instanceof TMDO_HivePress_Adapter ? $adapter->migrations() : array(); if ( empty( $mods ) ) { WP_CLI::log( sprintf( ' %s: no FSM modules', $slug ) ); continue; } WP_CLI::log( sprintf( ' %s: %s', $slug, implode( ', ', $mods ) ) ); } WP_CLI::log( '' ); WP_CLI::log( 'Run `wp wpdo benchmark ` for per-module timings.' ); } // ── Internal helpers ──────────────────────────────────────────────── /** * Apply a target FSM state to all modules owned by an addon's adapter. * * @param string $slug Addon slug. * @param string $state Target state. */ private function set_state( string $slug, string $state ): void { if ( '' === $slug ) { WP_CLI::error( 'Missing addon slug.' ); } if ( ! in_array( $state, TMDO_Feature_Flags::VALID_STATES, true ) ) { WP_CLI::error( sprintf( 'Invalid state "%s". Valid: %s', $state, implode( ', ', TMDO_Feature_Flags::VALID_STATES ) ) ); } $adapter = class_exists( 'TMDO_HivePress_Bootstrap' ) ? TMDO_HivePress_Bootstrap::adapter_for( $slug ) : null; if ( ! $adapter instanceof TMDO_HivePress_Adapter ) { WP_CLI::error( sprintf( 'Adapter "%s" not active.', $slug ) ); } $modules = $adapter->migrations(); if ( empty( $modules ) ) { WP_CLI::warning( sprintf( 'Adapter "%s" has no FSM modules.', $slug ) ); return; } foreach ( $modules as $module ) { TMDO_Feature_Flags::set( (string) $module, $state ); WP_CLI::log( sprintf( ' %s → %s', $module, $state ) ); } WP_CLI::success( sprintf( 'Adapter "%s" modules set to %s.', $slug, $state ) ); } } } // end if ( ! class_exists )