@@ -198,6 +198,11 @@ public function worktree_prune(): array {
198198 * @return array<string,mixed>|\WP_Error
199199 */
200200 public function read_file ( string $ handle , string $ path , int $ max_size , ?int $ offset = null , ?int $ limit = null ): array |\WP_Error {
201+ $ policy_error = WorkspaceAliasResolver::read_error_if_disallowed ($ handle , $ path );
202+ if ( null !== $ policy_error ) {
203+ return $ policy_error ;
204+ }
205+
201206 $ context = $ this ->resolve_handle ($ handle );
202207 if ( is_wp_error ($ context ) ) {
203208 return $ context ;
@@ -234,13 +239,18 @@ public function read_file( string $handle, string $path, int $max_size, ?int $of
234239 $ result_content = implode ("\n" , $ lines );
235240 }
236241
237- return array (
242+ $ result = array (
238243 'success ' => true ,
239244 'backend ' => 'github_api ' ,
240245 'content ' => $ result_content ,
241246 'path ' => $ path ,
242247 'size ' => $ size ,
243248 );
249+ if ( WorkspaceAliasResolver::is_context_repository ($ handle ) ) {
250+ $ result ['workspace_policy ' ] = WorkspaceAliasResolver::policy_attestation ($ handle );
251+ }
252+
253+ return $ result ;
244254 }
245255
246256 /**
@@ -249,6 +259,11 @@ public function read_file( string $handle, string $path, int $max_size, ?int $of
249259 * @return array<string,mixed>|\WP_Error
250260 */
251261 public function list_directory ( string $ handle , ?string $ path = null ): array |\WP_Error {
262+ $ policy_error = WorkspaceAliasResolver::read_error_if_disallowed ($ handle , $ path ?? '' );
263+ if ( null !== $ policy_error ) {
264+ return $ policy_error ;
265+ }
266+
252267 $ context = $ this ->resolve_handle ($ handle );
253268 if ( is_wp_error ($ context ) ) {
254269 return $ context ;
@@ -287,13 +302,20 @@ public function list_directory( string $handle, ?string $path = null ): array|\W
287302 );
288303 }
289304
290- return array (
305+ $ entries = WorkspaceAliasResolver::filter_context_entries ($ handle , '' === $ prefix ? '/ ' : $ prefix , $ entries );
306+
307+ $ result = array (
291308 'success ' => true ,
292309 'backend ' => 'github_api ' ,
293310 'repo ' => $ handle ,
294311 'path ' => '' === $ prefix ? '/ ' : $ prefix ,
295312 'entries ' => $ entries ,
296313 );
314+ if ( WorkspaceAliasResolver::is_context_repository ($ handle ) ) {
315+ $ result ['workspace_policy ' ] = WorkspaceAliasResolver::policy_attestation ($ handle );
316+ }
317+
318+ return $ result ;
297319 }
298320
299321 /**
@@ -302,6 +324,11 @@ public function list_directory( string $handle, ?string $path = null ): array|\W
302324 * @return array<string,mixed>|\WP_Error
303325 */
304326 public function grep ( string $ handle , string $ pattern , ?string $ path = null , ?string $ include_pattern = null , int $ max_results = 100 , int $ context_lines = 0 ): array |\WP_Error {
327+ $ policy_error = WorkspaceAliasResolver::read_error_if_disallowed ($ handle , $ path ?? '' );
328+ if ( null !== $ policy_error ) {
329+ return $ policy_error ;
330+ }
331+
305332 $ context = $ this ->resolve_handle ($ handle );
306333 if ( is_wp_error ($ context ) ) {
307334 return $ context ;
@@ -354,6 +381,10 @@ public function grep( string $handle, string $pattern, ?string $path = null, ?st
354381
355382 foreach ( $ files as $ file ) {
356383 $ file_path = (string ) ( $ file ['path ' ] ?? '' );
384+ $ context_policy = WorkspaceAliasResolver::context_policy_for ($ handle );
385+ if ( null !== $ context_policy && ! WorkspaceAliasResolver::path_allowed_by_policy ($ file_path , $ context_policy ) ) {
386+ continue ;
387+ }
357388 if ( '' === $ file_path || isset ($ seen [ $ file_path ]) || ! $ this ->path_matches_include ($ file_path , $ include_pattern ) ) {
358389 continue ;
359390 }
@@ -380,7 +411,7 @@ public function grep( string $handle, string $pattern, ?string $path = null, ?st
380411 }
381412 }
382413
383- return array (
414+ $ result = array (
384415 'success ' => true ,
385416 'backend ' => 'github_api ' ,
386417 'repo ' => $ handle ,
@@ -390,6 +421,11 @@ public function grep( string $handle, string $pattern, ?string $path = null, ?st
390421 'count ' => count ($ matches ),
391422 'truncated ' => count ($ matches ) >= $ max_results ,
392423 );
424+ if ( WorkspaceAliasResolver::is_context_repository ($ handle ) ) {
425+ $ result ['workspace_policy ' ] = WorkspaceAliasResolver::policy_attestation ($ handle );
426+ }
427+
428+ return $ result ;
393429 }
394430
395431 /**
@@ -398,6 +434,10 @@ public function grep( string $handle, string $pattern, ?string $path = null, ?st
398434 * @return array<string,mixed>|\WP_Error
399435 */
400436 public function write_file ( string $ handle , string $ path , string $ content ): array |\WP_Error {
437+ if ( WorkspaceAliasResolver::is_context_repository ($ handle ) ) {
438+ return WorkspaceAliasResolver::mutation_error ($ handle , 'write ' );
439+ }
440+
401441 $ context = $ this ->resolve_handle ($ handle );
402442 if ( is_wp_error ($ context ) ) {
403443 return $ context ;
@@ -428,6 +468,10 @@ public function write_file( string $handle, string $path, string $content ): arr
428468 * @return array<string,mixed>|\WP_Error
429469 */
430470 public function edit_file ( string $ handle , string $ path , string $ old_string , string $ new_string , bool $ replace_all = false ): array |\WP_Error {
471+ if ( WorkspaceAliasResolver::is_context_repository ($ handle ) ) {
472+ return WorkspaceAliasResolver::mutation_error ($ handle , 'edit ' );
473+ }
474+
431475 $ context = $ this ->resolve_handle ($ handle );
432476 if ( is_wp_error ($ context ) ) {
433477 return $ context ;
@@ -490,12 +534,13 @@ public function show( string $handle ): array|\WP_Error {
490534
491535 $ files = array_values (array_unique (array_values ( (array ) $ context ['changed_files ' ])));
492536
493- return array (
537+ $ result = array (
494538 'success ' => true ,
495539 'backend ' => 'github_api ' ,
496540 'name ' => $ handle ,
497541 'repo ' => $ context ['repo_name ' ],
498- 'is_worktree ' => isset ($ context ['branch ' ]) && '' !== (string ) $ context ['branch ' ],
542+ 'is_worktree ' => empty ($ context ['read_only_context ' ]) && isset ($ context ['branch ' ]) && '' !== (string ) $ context ['branch ' ],
543+ 'is_context ' => ! empty ($ context ['read_only_context ' ]),
499544 'path ' => 'github:// ' . $ context ['repo ' ] . ( '' !== (string ) $ context ['branch ' ]
500545 ? '# ' . $ context ['branch ' ]
501546 : '' ),
@@ -505,6 +550,11 @@ public function show( string $handle ): array|\WP_Error {
505550 'dirty ' => count ($ files ),
506551 'files ' => $ files ,
507552 );
553+ if ( WorkspaceAliasResolver::is_context_repository ($ handle ) ) {
554+ $ result ['workspace_policy ' ] = WorkspaceAliasResolver::policy_attestation ($ handle );
555+ }
556+
557+ return $ result ;
508558 }
509559
510560 /**
@@ -514,6 +564,10 @@ public function show( string $handle ): array|\WP_Error {
514564 */
515565 public function git_diff ( string $ handle , ?string $ from = null , ?string $ to = null , bool $ staged = false , ?string $ path = null ): array |\WP_Error {
516566 unset($ staged );
567+ $ policy_error = WorkspaceAliasResolver::read_error_if_disallowed ($ handle , $ path ?? '' );
568+ if ( null !== $ policy_error ) {
569+ return $ policy_error ;
570+ }
517571
518572 $ context = $ this ->resolve_handle ($ handle );
519573 if ( is_wp_error ($ context ) ) {
@@ -549,13 +603,18 @@ public function git_diff( string $handle, ?string $from = null, ?string $to = nu
549603 $ diff .= $ this ->build_unified_file_diff ($ changed_path , $ old_content , (string ) $ new_content );
550604 }
551605
552- return array (
606+ $ result = array (
553607 'success ' => true ,
554608 'backend ' => 'github_api ' ,
555609 'name ' => $ handle ,
556610 'repo ' => $ context ['repo_name ' ],
557611 'diff ' => $ diff ,
558612 );
613+ if ( WorkspaceAliasResolver::is_context_repository ($ handle ) ) {
614+ $ result ['workspace_policy ' ] = WorkspaceAliasResolver::policy_attestation ($ handle );
615+ }
616+
617+ return $ result ;
559618 }
560619
561620 /**
@@ -570,19 +629,25 @@ public function git_status( string $handle ): array|\WP_Error {
570629 }
571630
572631 $ files = array_values (array_unique (array_values ( (array ) $ context ['changed_files ' ])));
573- return array (
632+ $ result = array (
574633 'success ' => true ,
575634 'backend ' => 'github_api ' ,
576635 'name ' => $ handle ,
577636 'repo ' => $ context ['repo_name ' ],
578- 'is_worktree ' => true ,
637+ 'is_worktree ' => empty ($ context ['read_only_context ' ]),
638+ 'is_context ' => ! empty ($ context ['read_only_context ' ]),
579639 'path ' => 'github:// ' . $ context ['repo ' ] . '# ' . $ context ['branch ' ],
580640 'branch ' => $ context ['branch ' ],
581641 'remote ' => 'https://github.com/ ' . $ context ['repo ' ] . '.git ' ,
582642 'commit ' => '' !== $ context ['last_commit_sha ' ] ? $ context ['last_commit_sha ' ] : null ,
583643 'dirty ' => count ($ files ),
584644 'files ' => $ files ,
585645 );
646+ if ( WorkspaceAliasResolver::is_context_repository ($ handle ) ) {
647+ $ result ['workspace_policy ' ] = WorkspaceAliasResolver::policy_attestation ($ handle );
648+ }
649+
650+ return $ result ;
586651 }
587652
588653 /**
@@ -591,6 +656,10 @@ public function git_status( string $handle ): array|\WP_Error {
591656 * @return array<string,mixed>|\WP_Error
592657 */
593658 public function git_add ( string $ handle , array $ paths ): array |\WP_Error {
659+ if ( WorkspaceAliasResolver::is_context_repository ($ handle ) ) {
660+ return WorkspaceAliasResolver::mutation_error ($ handle , 'git add ' );
661+ }
662+
594663 $ context = $ this ->resolve_handle ($ handle );
595664 if ( is_wp_error ($ context ) ) {
596665 return $ context ;
@@ -611,6 +680,10 @@ public function git_add( string $handle, array $paths ): array|\WP_Error {
611680 * @return array<string,mixed>|\WP_Error
612681 */
613682 public function git_commit ( string $ handle , string $ message ): array |\WP_Error {
683+ if ( WorkspaceAliasResolver::is_context_repository ($ handle ) ) {
684+ return WorkspaceAliasResolver::mutation_error ($ handle , 'git commit ' );
685+ }
686+
614687 $ context = $ this ->resolve_handle ($ handle );
615688 if ( is_wp_error ($ context ) ) {
616689 return $ context ;
@@ -1103,6 +1176,10 @@ private function group_diff_hunks( array $ops, int $context_lines ): array {
11031176 * @return array<string,mixed>|\WP_Error
11041177 */
11051178 public function git_push ( string $ handle , string $ remote = 'origin ' , ?string $ branch = null ): array |\WP_Error {
1179+ if ( WorkspaceAliasResolver::is_context_repository ($ handle ) ) {
1180+ return WorkspaceAliasResolver::mutation_error ($ handle , 'git push ' );
1181+ }
1182+
11061183 $ context = $ this ->resolve_handle ($ handle );
11071184 if ( is_wp_error ($ context ) ) {
11081185 return $ context ;
@@ -1131,6 +1208,26 @@ public function git_push( string $handle, string $remote = 'origin', ?string $br
11311208 * @return array<string,mixed>
11321209 */
11331210 private function resolve_handle ( string $ handle ): array |\WP_Error {
1211+ $ context_policy = WorkspaceAliasResolver::context_policy_for ($ handle );
1212+ if ( null !== $ context_policy ) {
1213+ $ repo = (string ) ( $ context_policy ['repo ' ] ?? '' );
1214+ if ( '' === $ repo ) {
1215+ $ repo = (string ) ( $ context_policy ['target ' ] ?? $ handle );
1216+ }
1217+
1218+ return array (
1219+ 'handle ' => (string ) $ context_policy ['alias ' ],
1220+ 'repo_name ' => (string ) $ context_policy ['alias ' ],
1221+ 'repo ' => $ repo ,
1222+ 'branch ' => (string ) ( $ context_policy ['ref ' ] ?? '' ),
1223+ 'read_ref ' => (string ) ( $ context_policy ['ref ' ] ?? '' ),
1224+ 'pending_files ' => array (),
1225+ 'changed_files ' => array (),
1226+ 'last_commit_sha ' => '' ,
1227+ 'read_only_context ' => true ,
1228+ );
1229+ }
1230+
11341231 $ handle = $ this ->resolve_alias ($ handle );
11351232 $ state = $ this ->state ();
11361233 if ( isset ($ state ['worktrees ' ][ $ handle ]) ) {
0 commit comments