1717
1818 $ GLOBALS ['dmc_workspace_alias_filters ' ] = array ();
1919 $ GLOBALS ['dmc_workspace_alias_ability ' ] = null ;
20+ $ GLOBALS ['dmc_workspace_alias_registered_abilities ' ] = array ();
21+ $ GLOBALS ['dmc_workspace_alias_remote_edit_input ' ] = array ();
2022
2123 function add_filter ( string $ tag , callable $ callback , int $ priority = 10 , int $ accepted_args = 1 ): void
2224 {
@@ -49,9 +51,82 @@ function wp_get_ability( string $name )
4951 return $ GLOBALS ['dmc_workspace_alias_ability ' ];
5052 }
5153
54+ function wp_register_ability ( string $ name , array $ definition ): void
55+ {
56+ $ GLOBALS ['dmc_workspace_alias_registered_abilities ' ][ $ name ] = $ definition ;
57+ }
58+
59+ function add_action ( string $ hook , callable $ callback , int $ priority = 10 ): void
60+ {
61+ }
62+
63+ function doing_action ( string $ hook ): bool
64+ {
65+ return 'wp_abilities_api_init ' === $ hook ;
66+ }
67+
5268 function is_wp_error ( $ value ): bool
5369 {
54- return false ;
70+ return $ value instanceof WP_Error;
71+ }
72+
73+ class WP_Error
74+ {
75+ private string $ code ;
76+ private string $ message ;
77+
78+ public function __construct ( string $ code , string $ message , array $ data = array () )
79+ {
80+ $ this ->code = $ code ;
81+ $ this ->message = $ message ;
82+ }
83+
84+ public function get_error_code (): string
85+ {
86+ return $ this ->code ;
87+ }
88+
89+ public function get_error_message (): string
90+ {
91+ return $ this ->message ;
92+ }
93+ }
94+ }
95+
96+ namespace DataMachine \Abilities {
97+ class PermissionHelper
98+ {
99+ public static function can_manage (): bool
100+ {
101+ return true ;
102+ }
103+ }
104+ }
105+
106+ namespace DataMachineCode \Workspace {
107+ class Workspace
108+ {
109+ public const ARTIFACT_CLEANUP_DEFAULT_LIMIT = 100 ;
110+ public const MAX_READ_SIZE = 1048576 ;
111+ }
112+
113+ class RemoteWorkspaceBackend
114+ {
115+ public static function should_handle (): bool
116+ {
117+ return true ;
118+ }
119+
120+ public function edit_file ( string $ handle , string $ path , string $ old_string , string $ new_string , bool $ replace_all = false ): array
121+ {
122+ $ GLOBALS ['dmc_workspace_alias_remote_edit_input ' ] = compact ('handle ' , 'path ' , 'old_string ' , 'new_string ' , 'replace_all ' );
123+ return array (
124+ 'success ' => true ,
125+ 'name ' => $ handle ,
126+ 'path ' => $ path ,
127+ 'replacements ' => 1 ,
128+ );
129+ }
55130 }
56131}
57132
@@ -74,6 +149,7 @@ protected function buildErrorResponse( string $message, string $tool_name ): arr
74149
75150namespace {
76151 include __DIR__ . '/../inc/Workspace/WorkspaceAliasResolver.php ' ;
152+ include __DIR__ . '/../inc/Abilities/WorkspaceAbilities.php ' ;
77153 include __DIR__ . '/../inc/Tools/WorkspaceTools.php ' ;
78154 include __DIR__ . '/../inc/Tools/WorkspaceDiffTools.php ' ;
79155
@@ -130,12 +206,22 @@ public function execute( array $input ): array
130206 $ ability = new DataMachineCodeWorkspaceAliasFakeAbility ();
131207 $ GLOBALS ['dmc_workspace_alias_ability ' ] = $ ability ;
132208 $ tools = new \DataMachineCode \Tools \WorkspaceTools ();
209+ new \DataMachineCode \Abilities \WorkspaceAbilities ();
133210 $ read_definition = $ tools ->getReadDefinition ();
134211 $ grep_definition = $ tools ->getGrepDefinition ();
212+ $ edit_definition = $ tools ->getEditDefinition ();
135213 $ git_diff_definition = $ tools ->getGitDiffDefinition ();
214+ $ ability_edit_schema = $ GLOBALS ['dmc_workspace_alias_registered_abilities ' ]['datamachine/workspace-edit ' ]['input_schema ' ] ?? array ();
136215 $ assert ('workspace_read schema allows path-only mounted workspace calls ' , array ( 'path ' ) === ( $ read_definition ['parameters ' ]['required ' ] ?? null ));
137216 $ assert ('workspace_grep schema allows path-only mounted workspace calls ' , array ( 'pattern ' ) === ( $ grep_definition ['parameters ' ]['required ' ] ?? null ));
138217 $ assert ('workspace_git_diff schema allows path-only mounted workspace calls ' , array () === ( $ git_diff_definition ['parameters ' ]['required ' ] ?? null ));
218+ $ assert ('workspace_edit wrapper schema keeps edit text optional for aliases ' , array ( 'path ' ) === ( $ edit_definition ['parameters ' ]['required ' ] ?? null ));
219+ $ assert ('workspace_edit ability schema keeps edit text optional for aliases ' , array ( 'path ' ) === ( $ ability_edit_schema ['required ' ] ?? null ));
220+ foreach ( array ( 'old_string ' , 'new_string ' , 'search ' , 'replace ' , 'old ' , 'new ' ) as $ property ) {
221+ $ assert ("workspace_edit wrapper schema exposes {$ property }" , isset ($ edit_definition ['parameters ' ]['properties ' ][ $ property ]));
222+ $ assert ("workspace_edit ability schema exposes {$ property }" , isset ($ ability_edit_schema ['properties ' ][ $ property ]));
223+ }
224+ $ assert ('workspace_edit schema does not expose broad find alias ' , ! isset ($ edit_definition ['parameters ' ]['properties ' ]['find ' ]) && ! isset ($ ability_edit_schema ['properties ' ]['find ' ]));
139225
140226 $ absolute_read = $ tools ->handleRead (array ( 'path ' => '/workspace/homeboy-extensions/wordpress/scripts/build/build.sh ' ));
141227 $ assert ('absolute workspace path read succeeds ' , true === ( $ absolute_read ['success ' ] ?? false ));
@@ -174,6 +260,30 @@ public function execute( array $input ): array
174260 $ assert ('workspace_edit maps old to old_string ' , 'npm install --silent ' === ( $ ability ->last_input ['old_string ' ] ?? '' ));
175261 $ assert ('workspace_edit maps new to new_string ' , 'npm install --legacy-peer-deps ' === ( $ ability ->last_input ['new_string ' ] ?? '' ));
176262
263+ $ ability_mounted_edit = \DataMachineCode \Abilities \WorkspaceAbilities::editFile (
264+ array (
265+ 'path ' => '/workspace/homeboy-extensions/wordpress/scripts/build/build.sh ' ,
266+ 'old ' => 'npm install --silent ' ,
267+ 'new ' => 'npm install --legacy-peer-deps ' ,
268+ )
269+ );
270+ $ assert ('workspace_edit ability accepts mounted absolute path ' , ! is_wp_error ($ ability_mounted_edit ) && true === ( $ ability_mounted_edit ['success ' ] ?? false ));
271+ $ assert ('workspace_edit ability infers repo from mounted path ' , 'homeboy-extensions ' === ( $ GLOBALS ['dmc_workspace_alias_remote_edit_input ' ]['handle ' ] ?? '' ));
272+ $ assert ('workspace_edit ability converts mounted path to relative path ' , 'wordpress/scripts/build/build.sh ' === ( $ GLOBALS ['dmc_workspace_alias_remote_edit_input ' ]['path ' ] ?? '' ));
273+ $ assert ('workspace_edit ability maps old alias to old_string ' , 'npm install --silent ' === ( $ GLOBALS ['dmc_workspace_alias_remote_edit_input ' ]['old_string ' ] ?? '' ));
274+ $ assert ('workspace_edit ability maps new alias to new_string ' , 'npm install --legacy-peer-deps ' === ( $ GLOBALS ['dmc_workspace_alias_remote_edit_input ' ]['new_string ' ] ?? '' ));
275+
276+ $ unsupported_alias = \DataMachineCode \Abilities \WorkspaceAbilities::editFile (
277+ array (
278+ 'repo ' => 'homeboy-extensions ' ,
279+ 'path ' => 'wordpress/scripts/build/build.sh ' ,
280+ 'find ' => 'npm install --silent ' ,
281+ 'replace ' => 'npm install --legacy-peer-deps ' ,
282+ )
283+ );
284+ $ assert ('unsupported edit aliases fail at ability layer ' , is_wp_error ($ unsupported_alias ));
285+ $ assert ('unsupported edit alias error is actionable ' , is_wp_error ($ unsupported_alias ) && 'old_string is required. ' === $ unsupported_alias ->get_error_message ());
286+
177287 $ result = $ tools ->handleGitStatus (array ( 'name ' => 'current-project ' ));
178288 $ data = $ result ['data ' ] ?? array ();
179289
0 commit comments