@@ -1562,29 +1562,33 @@ private function registerAbilities(): void {
15621562 'input_schema ' => array (
15631563 'type ' => 'object ' ,
15641564 'properties ' => array (
1565- 'mode ' => array (
1565+ 'mode ' => array (
15661566 'type ' => 'string ' ,
1567- 'description ' => 'Cleanup mode: inventory, artifacts, retention, or emergency. ' ,
1567+ 'description ' => 'Cleanup mode: inventory, artifacts, retention, stale-worktrees, or emergency. ' ,
15681568 ),
1569- 'force ' => array (
1569+ 'force ' => array (
15701570 'type ' => 'boolean ' ,
15711571 'description ' => 'Forward force=true to cleanup tasks that support it. ' ,
15721572 ),
1573- 'dry_run ' => array (
1573+ 'dry_run ' => array (
15741574 'type ' => 'boolean ' ,
15751575 'description ' => 'Rejected for background cleanup scheduling; use review abilities for dry-runs. ' ,
15761576 ),
1577- 'older_than ' => array (
1577+ 'older_than ' => array (
15781578 'type ' => 'string ' ,
15791579 'description ' => 'Optional worktree retention age gate such as 14d. ' ,
15801580 ),
1581- 'source ' => array (
1581+ 'worktree_stale_only ' => array (
1582+ 'type ' => 'boolean ' ,
1583+ 'description ' => 'Only plan stale/inactive worktrees for destructive removal. ' ,
1584+ ),
1585+ 'source ' => array (
15821586 'type ' => 'string ' ,
15831587 'description ' => 'Caller source marker. ' ,
15841588 ),
1585- 'user_id ' => array ( 'type ' => 'integer ' ),
1586- 'agent_id ' => array ( 'type ' => 'integer ' ),
1587- 'agent_slug ' => array ( 'type ' => 'string ' ),
1589+ 'user_id ' => array ( 'type ' => 'integer ' ),
1590+ 'agent_id ' => array ( 'type ' => 'integer ' ),
1591+ 'agent_slug ' => array ( 'type ' => 'string ' ),
15881592 ),
15891593 ),
15901594 'output_schema ' => array (
@@ -2327,18 +2331,20 @@ private function registerAbilities(): void {
23272331 'force_artifact_cleanup ' => array ( 'type ' => 'boolean ' ),
23282332 'worktree_older_than ' => array ( 'type ' => 'string ' ),
23292333 'worktree_sort ' => array ( 'type ' => 'string ' ),
2334+ 'worktree_stale_only ' => array ( 'type ' => 'boolean ' ),
23302335 'plan ' => array ( 'type ' => 'object ' ),
23312336 ),
23322337 ),
23332338 'output_schema ' => array (
23342339 'type ' => 'object ' ,
23352340 'properties ' => array (
2336- 'success ' => array ( 'type ' => 'boolean ' ),
2337- 'mode ' => array ( 'type ' => 'string ' ),
2338- 'plan_id ' => array ( 'type ' => 'string ' ),
2339- 'rows ' => array ( 'type ' => 'object ' ),
2340- 'chunks ' => array ( 'type ' => 'array ' ),
2341- 'summary ' => array ( 'type ' => 'object ' ),
2341+ 'success ' => array ( 'type ' => 'boolean ' ),
2342+ 'mode ' => array ( 'type ' => 'string ' ),
2343+ 'plan_id ' => array ( 'type ' => 'string ' ),
2344+ 'rows ' => array ( 'type ' => 'object ' ),
2345+ 'action_rows ' => array ( 'type ' => 'object ' ),
2346+ 'chunks ' => array ( 'type ' => 'array ' ),
2347+ 'summary ' => array ( 'type ' => 'object ' ),
23422348 ),
23432349 ),
23442350 'execute_callback ' => array ( self ::class, 'workspaceCleanupPlan ' ),
@@ -2493,14 +2499,21 @@ public static function getCapabilities( array $input ): array { // phpcs:ignor
24932499 * @return array Result.
24942500 */
24952501 public static function showRepo ( array $ input ): array |\WP_Error {
2502+ $ workspace = new Workspace ();
2503+ if ( RemoteWorkspaceBackend::should_handle () ) {
2504+ $ local_result = self ::showLocalWorkspaceHandleIfPresent ($ workspace , (string ) ( $ input ['name ' ] ?? '' ));
2505+ if ( null !== $ local_result ) {
2506+ return $ local_result ;
2507+ }
2508+ }
2509+
24962510 if ( RemoteWorkspaceBackend::should_handle () ) {
24972511 $ result = ( new RemoteWorkspaceBackend () )->show ($ input ['name ' ] ?? '' );
24982512 if ( ! self ::shouldFallbackToLocalWorkspace ($ result ) ) {
24992513 return $ result ;
25002514 }
25012515 }
25022516
2503- $ workspace = new Workspace ();
25042517 return $ workspace ->show_repo ($ input ['name ' ] ?? '' );
25052518 }
25062519
@@ -3461,17 +3474,34 @@ public static function worktreeAdd( array $input ): array|\WP_Error {
34613474 * Whether a repo argument resolves to an editable local primary checkout.
34623475 */
34633476 private static function hasLocalPrimaryCheckout ( Workspace $ workspace , string $ repo ): bool {
3464- if ( '' === trim ($ repo ) ) {
3477+ $ result = self ::showLocalWorkspaceHandleIfPresent ($ workspace , $ repo );
3478+ if ( null === $ result ) {
34653479 return false ;
34663480 }
34673481
3468- $ result = $ workspace ->show_repo ($ repo );
3482+ $ path = (string ) ( $ result ['path ' ] ?? '' );
3483+ return ! str_contains (basename ($ path ), '@ ' );
3484+ }
3485+
3486+ /**
3487+ * Return local workspace details for an existing local handle, if present.
3488+ */
3489+ private static function showLocalWorkspaceHandleIfPresent ( Workspace $ workspace , string $ handle ): ?array {
3490+ if ( '' === trim ($ handle ) ) {
3491+ return null ;
3492+ }
3493+
3494+ $ result = $ workspace ->show_repo ($ handle );
34693495 if ( is_wp_error ($ result ) ) {
3470- return false ;
3496+ return null ;
34713497 }
34723498
34733499 $ path = (string ) ( $ result ['path ' ] ?? '' );
3474- return '' !== $ path && ! str_starts_with ($ path , 'github:// ' ) && ! str_contains (basename ($ path ), '@ ' );
3500+ if ( '' === $ path || str_starts_with ($ path , 'github:// ' ) ) {
3501+ return null ;
3502+ }
3503+
3504+ return $ result ;
34753505 }
34763506
34773507 /**
@@ -3585,7 +3615,7 @@ public static function workspaceCleanupRun( array $input ): array|\WP_Error {
35853615
35863616 $ mode = strtolower (preg_replace ('/[^a-z0-9_\-]/ ' , '' , (string ) ( $ input ['mode ' ] ?? 'retention ' )));
35873617 $ map = array (
3588- 'inventory ' => array (
3618+ 'inventory ' => array (
35893619 'task_type ' => 'workspace_hygiene_report ' ,
35903620 'params ' => array (
35913621 'include_cleanup ' => true ,
@@ -3594,7 +3624,7 @@ public static function workspaceCleanupRun( array $input ): array|\WP_Error {
35943624 'size_limit ' => 200 ,
35953625 ),
35963626 ),
3597- 'artifacts ' => array (
3627+ 'artifacts ' => array (
35983628 'task_type ' => 'workspace_retention_cleanup ' ,
35993629 'params ' => array (
36003630 'dry_run ' => false ,
@@ -3603,7 +3633,18 @@ public static function workspaceCleanupRun( array $input ): array|\WP_Error {
36033633 'skip_github ' => true ,
36043634 ),
36053635 ),
3606- 'retention ' => array (
3636+ 'stale-worktrees ' => array (
3637+ 'task_type ' => 'workspace_retention_cleanup ' ,
3638+ 'params ' => array (
3639+ 'dry_run ' => false ,
3640+ 'artifact_cleanup ' => false ,
3641+ 'worktree_cleanup ' => true ,
3642+ 'skip_github ' => true ,
3643+ 'worktree_older_than ' => '14d ' ,
3644+ 'worktree_stale_only ' => true ,
3645+ ),
3646+ ),
3647+ 'retention ' => array (
36073648 'task_type ' => 'workspace_retention_cleanup ' ,
36083649 'params ' => array (
36093650 'dry_run ' => false ,
@@ -3613,7 +3654,7 @@ public static function workspaceCleanupRun( array $input ): array|\WP_Error {
36133654 'worktree_older_than ' => '14d ' ,
36143655 ),
36153656 ),
3616- 'emergency ' => array (
3657+ 'emergency ' => array (
36173658 'task_type ' => 'workspace_disk_emergency_cleanup ' ,
36183659 'params ' => array (
36193660 'artifact_chunk_size ' => 10 ,
@@ -3639,6 +3680,9 @@ public static function workspaceCleanupRun( array $input ): array|\WP_Error {
36393680 if ( isset ($ input ['older_than ' ]) && '' !== trim ( (string ) $ input ['older_than ' ]) ) {
36403681 $ params ['worktree_older_than ' ] = trim ( (string ) $ input ['older_than ' ]);
36413682 }
3683+ if ( isset ($ input ['worktree_stale_only ' ]) ) {
3684+ $ params ['worktree_stale_only ' ] = (bool ) $ input ['worktree_stale_only ' ];
3685+ }
36423686 if ( 'artifacts ' === $ mode ) {
36433687 if ( isset ($ input ['limit ' ]) ) {
36443688 $ params ['limit ' ] = (int ) $ input ['limit ' ];
@@ -4034,6 +4078,7 @@ public static function workspaceCleanupPlan( array $input ): array|\WP_Error {
40344078 'force_artifact_cleanup ' => ! empty ($ input ['force_artifact_cleanup ' ]),
40354079 'include_resolvers ' => ! empty ($ input ['include_resolvers ' ]),
40364080 'mode ' => (string ) ( $ input ['mode ' ] ?? 'cleanup_plan ' ),
4081+ 'worktree_stale_only ' => ! empty ($ input ['worktree_stale_only ' ]),
40374082 );
40384083 foreach ( array ( 'include_artifacts ' , 'include_worktrees ' ) as $ key ) {
40394084 if ( array_key_exists ($ key , $ input ) ) {
0 commit comments