@@ -602,6 +602,10 @@ public function adopt_repo( array $args, array $assoc_args ): void {
602602 * [--older-than=<duration>]
603603 * : Pass an age gate such as 7d or 24h into cleanup task params.
604604 *
605+ * [--top=<count>]
606+ * : For `plan`, number of largest reclaimable paths to show in the upfront
607+ * summary. Defaults to 10.
608+ *
605609 * [--limit=<count>]
606610 * : For DB-backed `apply` / `resume`, maximum pending rows to process in this
607611 * invocation (default 25, max 100). For `--mode=artifacts` pages, maximum
@@ -973,6 +977,9 @@ private function cleanup_plan_input( string $mode, array $assoc_args ): array {
973977 if ( isset ($ assoc_args ['older-than ' ]) && '' !== trim ( (string ) $ assoc_args ['older-than ' ]) ) {
974978 $ input ['worktree_older_than ' ] = trim ( (string ) $ assoc_args ['older-than ' ]);
975979 }
980+ if ( isset ($ assoc_args ['top ' ]) ) {
981+ $ input ['top_n ' ] = (int ) $ assoc_args ['top ' ];
982+ }
976983 if ( isset ($ assoc_args ['force ' ]) ) {
977984 $ input ['force_artifact_cleanup ' ] = (bool ) $ assoc_args ['force ' ];
978985 }
@@ -1428,12 +1435,131 @@ private function render_cleanup_plan_result( array $result, array $assoc_args ):
14281435 WP_CLI ::log (sprintf ('Rows: %d ' , (int ) ( $ summary ['total_rows ' ] ?? 0 )));
14291436 WP_CLI ::log (sprintf ('Bytes: %s ' , $ this ->format_bytes ($ summary ['total_size_bytes ' ] ?? 0 )));
14301437 WP_CLI ::log (sprintf ('Apply: wp datamachine-code workspace cleanup apply %s ' , (string ) ( $ result ['run_id ' ] ?? '' )));
1438+ $ this ->render_cleanup_plan_category_totals ( (array ) ( $ summary ['category_totals ' ] ?? array () ) );
1439+ $ this ->render_cleanup_plan_top_reclaimable ( (array ) ( $ summary ['top_reclaimable ' ] ?? array () ) );
1440+ $ this ->render_cleanup_plan_blockers ( (array ) ( $ summary ['blockers ' ] ?? array () ) );
1441+ $ this ->render_cleanup_plan_recommended_commands ( (array ) ( $ summary ['recommended_commands ' ] ?? array () ), (string ) ( $ result ['run_id ' ] ?? '' ) );
14311442 $ inputs = (array ) ( $ result ['inputs ' ] ?? array () );
14321443 if ( empty ($ inputs ['include_artifacts ' ]) ) {
14331444 WP_CLI ::log ('Artifacts: skipped for bounded retention planning; run `wp datamachine-code workspace cleanup plan --mode=artifacts` when you want artifact rows. ' );
14341445 }
14351446 }
14361447
1448+ /**
1449+ * Render reclaimable cleanup bytes by category.
1450+ *
1451+ * @param array<string,int> $totals Category totals.
1452+ */
1453+ private function render_cleanup_plan_category_totals ( array $ totals ): void {
1454+ if ( array () === $ totals ) {
1455+ return ;
1456+ }
1457+
1458+ WP_CLI ::log ('' );
1459+ WP_CLI ::log ('Reclaimable space by category: ' );
1460+ $ labels = array (
1461+ 'whole_worktrees ' => 'whole worktrees ' ,
1462+ 'dependency_artifacts ' => 'dependency artifacts ' ,
1463+ 'build_outputs ' => 'build outputs ' ,
1464+ 'caches ' => 'caches ' ,
1465+ );
1466+ $ rows = array ();
1467+ foreach ( $ labels as $ category => $ label ) {
1468+ $ rows [] = array (
1469+ 'category ' => $ label ,
1470+ 'bytes ' => $ this ->format_bytes ($ totals [ $ category ] ?? 0 ),
1471+ );
1472+ }
1473+ $ this ->format_items ($ rows , array ( 'category ' , 'bytes ' ), array ( 'format ' => 'table ' ), 'category ' );
1474+ }
1475+
1476+ /**
1477+ * Render largest reclaimable paths.
1478+ *
1479+ * @param array<int,array<string,mixed>> $paths Top paths.
1480+ */
1481+ private function render_cleanup_plan_top_reclaimable ( array $ paths ): void {
1482+ if ( array () === $ paths ) {
1483+ return ;
1484+ }
1485+
1486+ WP_CLI ::log ('' );
1487+ WP_CLI ::log ('Top reclaimable paths: ' );
1488+ $ rows = array_map (
1489+ fn ( $ row ) => array (
1490+ 'size ' => $ this ->format_bytes ($ row ['size_bytes ' ] ?? 0 ),
1491+ 'category ' => (string ) ( $ row ['category ' ] ?? '' ),
1492+ 'risk ' => (string ) ( $ row ['safety_class ' ] ?? '' ),
1493+ 'handle ' => (string ) ( $ row ['handle ' ] ?? '' ),
1494+ 'path ' => (string ) ( $ row ['path ' ] ?? '' ),
1495+ ),
1496+ $ paths
1497+ );
1498+ $ this ->format_items ($ rows , array ( 'size ' , 'category ' , 'risk ' , 'handle ' , 'path ' ), array ( 'format ' => 'table ' ), 'size ' );
1499+ }
1500+
1501+ /**
1502+ * Render blockers grouped by reason and repo.
1503+ *
1504+ * @param array<string,array<string,mixed>> $blockers Blocker buckets.
1505+ */
1506+ private function render_cleanup_plan_blockers ( array $ blockers ): void {
1507+ if ( array () === $ blockers ) {
1508+ return ;
1509+ }
1510+
1511+ WP_CLI ::log ('' );
1512+ WP_CLI ::log ('Blockers by reason and repo: ' );
1513+ $ rows = array ();
1514+ foreach ( $ blockers as $ reason => $ bucket ) {
1515+ $ bucket = (array ) $ bucket ;
1516+ $ repos = array ();
1517+ foreach ( (array ) ( $ bucket ['repos ' ] ?? array () ) as $ repo => $ repo_bucket ) {
1518+ $ repo_bucket = (array ) $ repo_bucket ;
1519+ $ repos [] = sprintf ('%s=%d ' , (string ) $ repo , (int ) ( $ repo_bucket ['count ' ] ?? 0 ));
1520+ }
1521+ $ rows [] = array (
1522+ 'reason ' => (string ) $ reason ,
1523+ 'count ' => (int ) ( $ bucket ['count ' ] ?? 0 ),
1524+ 'bytes ' => $ this ->format_bytes ($ bucket ['size_bytes ' ] ?? 0 ),
1525+ 'repos ' => implode (', ' , array_slice ($ repos , 0 , 5 )),
1526+ 'examples ' => implode (', ' , array_slice (array_map ('strval ' , (array ) ( $ bucket ['examples ' ] ?? array () )), 0 , 5 )),
1527+ );
1528+ }
1529+ $ this ->format_items ($ rows , array ( 'reason ' , 'count ' , 'bytes ' , 'repos ' , 'examples ' ), array ( 'format ' => 'table ' ), 'reason ' );
1530+ }
1531+
1532+ /**
1533+ * Render directly executable recommended cleanup commands.
1534+ *
1535+ * @param array<int,array<string,string>> $commands Recommended commands.
1536+ * @param string $run_id Cleanup run ID.
1537+ */
1538+ private function render_cleanup_plan_recommended_commands ( array $ commands , string $ run_id ): void {
1539+ if ( array () === $ commands ) {
1540+ return ;
1541+ }
1542+
1543+ WP_CLI ::log ('' );
1544+ WP_CLI ::log ('Recommended commands: ' );
1545+ $ rows = array_map (
1546+ function ( $ row ) use ( $ run_id ): array {
1547+ $ row = (array ) $ row ;
1548+ $ command = (string ) ( $ row ['command ' ] ?? '' );
1549+ if ( '' !== $ run_id ) {
1550+ $ command = str_replace ('<run-id> ' , $ run_id , $ command );
1551+ }
1552+ return array (
1553+ 'label ' => (string ) ( $ row ['label ' ] ?? '' ),
1554+ 'risk ' => (string ) ( $ row ['risk ' ] ?? '' ),
1555+ 'command ' => $ command ,
1556+ );
1557+ },
1558+ $ commands
1559+ );
1560+ $ this ->format_items ($ rows , array ( 'label ' , 'risk ' , 'command ' ), array ( 'format ' => 'table ' ), 'label ' );
1561+ }
1562+
14371563 private function cleanup_run_id ( int $ job_id ): string {
14381564 return 'cleanup-run- ' . $ job_id ;
14391565 }
0 commit comments