From f97d982d341ba9608714dc4db2fd263826480fb4 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Sun, 29 Mar 2026 00:01:32 +0100 Subject: [PATCH 1/5] Try support PowerShell --- src/WP_CLI/Shell/REPL.php | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/WP_CLI/Shell/REPL.php b/src/WP_CLI/Shell/REPL.php index 5dcff93b..abc9162e 100644 --- a/src/WP_CLI/Shell/REPL.php +++ b/src/WP_CLI/Shell/REPL.php @@ -176,10 +176,12 @@ private function prompt() { } private static function create_prompt_cmd( $prompt, $history_path ) { - $prompt = escapeshellarg( $prompt ); - $history_path = escapeshellarg( $history_path ); + $is_windows = \WP_CLI\Utils\is_windows(); + if ( getenv( 'WP_CLI_CUSTOM_SHELL' ) ) { $shell_binary = (string) getenv( 'WP_CLI_CUSTOM_SHELL' ); + } elseif ( $is_windows ) { + $shell_binary = 'powershell.exe'; } elseif ( is_file( '/bin/bash' ) && is_readable( '/bin/bash' ) ) { // Prefer /bin/bash when available since we use bash-specific commands. $shell_binary = '/bin/bash'; @@ -191,10 +193,19 @@ private static function create_prompt_cmd( $prompt, $history_path ) { $shell_binary = 'bash'; } - if ( ! is_file( $shell_binary ) || ! is_readable( $shell_binary ) ) { - WP_CLI::error( "The shell binary '{$shell_binary}' is not valid. You can override the shell to be used through the WP_CLI_CUSTOM_SHELL environment variable." ); + $is_powershell = $is_windows && 'powershell.exe' === $shell_binary; + + if ( $is_powershell ) { + // PowerShell uses ` (backtick) for escaping but for strings single quotes are literal. + // If prompt contains single quotes, we double them in PowerShell. + $prompt_for_ps = str_replace( "'", "''", $prompt ); + $cmd = "\$line = Read-Host -Prompt '{$prompt_for_ps}'; Write-Output \$line;"; + return "powershell.exe -NoProfile -Command \"{$cmd}\""; } + $prompt = escapeshellarg( $prompt ); + $history_path = escapeshellarg( $history_path ); + $is_ksh = self::is_ksh_shell( $shell_binary ); $shell_binary = escapeshellarg( $shell_binary ); From 1801a737ba7fbeccced12c1425e4e3dbc74473b7 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Sun, 29 Mar 2026 00:03:26 +0100 Subject: [PATCH 2/5] Lint fix --- src/WP_CLI/Shell/REPL.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/WP_CLI/Shell/REPL.php b/src/WP_CLI/Shell/REPL.php index abc9162e..998baa40 100644 --- a/src/WP_CLI/Shell/REPL.php +++ b/src/WP_CLI/Shell/REPL.php @@ -199,7 +199,7 @@ private static function create_prompt_cmd( $prompt, $history_path ) { // PowerShell uses ` (backtick) for escaping but for strings single quotes are literal. // If prompt contains single quotes, we double them in PowerShell. $prompt_for_ps = str_replace( "'", "''", $prompt ); - $cmd = "\$line = Read-Host -Prompt '{$prompt_for_ps}'; Write-Output \$line;"; + $cmd = "\$line = Read-Host -Prompt '{$prompt_for_ps}'; Write-Output \$line;"; return "powershell.exe -NoProfile -Command \"{$cmd}\""; } From 4a21ed1b2be0d90e1306e0009b28a0bea5e89118 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Sun, 29 Mar 2026 12:19:13 +0200 Subject: [PATCH 3/5] Update src/WP_CLI/Shell/REPL.php --- src/WP_CLI/Shell/REPL.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/WP_CLI/Shell/REPL.php b/src/WP_CLI/Shell/REPL.php index 998baa40..9ecba756 100644 --- a/src/WP_CLI/Shell/REPL.php +++ b/src/WP_CLI/Shell/REPL.php @@ -203,6 +203,10 @@ private static function create_prompt_cmd( $prompt, $history_path ) { return "powershell.exe -NoProfile -Command \"{$cmd}\""; } + if ( ! is_file( $shell_binary ) || ! is_readable( $shell_binary ) ) { + WP_CLI::error( "The shell binary '{$shell_binary}' is not valid. You can override the shell to be used through the WP_CLI_CUSTOM_SHELL environment variable." ); + } + $prompt = escapeshellarg( $prompt ); $history_path = escapeshellarg( $history_path ); From 23a8ee6a1b547c7f20548e0eeb035efd99003837 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Sun, 29 Mar 2026 13:03:49 +0200 Subject: [PATCH 4/5] make history --- src/WP_CLI/Shell/REPL.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/WP_CLI/Shell/REPL.php b/src/WP_CLI/Shell/REPL.php index 9ecba756..69bf04b5 100644 --- a/src/WP_CLI/Shell/REPL.php +++ b/src/WP_CLI/Shell/REPL.php @@ -198,9 +198,10 @@ private static function create_prompt_cmd( $prompt, $history_path ) { if ( $is_powershell ) { // PowerShell uses ` (backtick) for escaping but for strings single quotes are literal. // If prompt contains single quotes, we double them in PowerShell. - $prompt_for_ps = str_replace( "'", "''", $prompt ); - $cmd = "\$line = Read-Host -Prompt '{$prompt_for_ps}'; Write-Output \$line;"; - return "powershell.exe -NoProfile -Command \"{$cmd}\""; + $prompt_for_ps = str_replace( "'", "''", $prompt ); + $history_path_for_ps = str_replace( "'", "''", $history_path ); + $cmd = "\$line = Read-Host -Prompt '{$prompt_for_ps}'; if ( \$line ) { Add-Content -Path '{$history_path_for_ps}' -Value \$line; } Write-Output \$line;"; + return "powershell.exe -Command \"{$cmd}\""; } if ( ! is_file( $shell_binary ) || ! is_readable( $shell_binary ) ) { From c0c0bc6fe122d829e9a8a5b729a1662923f78d10 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Sun, 29 Mar 2026 14:01:33 +0200 Subject: [PATCH 5/5] no /dev/null --- features/shell.feature | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/features/shell.feature b/features/shell.feature index dfb63c44..a60c17d3 100644 --- a/features/shell.feature +++ b/features/shell.feature @@ -3,8 +3,12 @@ Feature: WordPress REPL Scenario: Blank session Given a WP install - When I run `wp shell < /dev/null` - And I run `wp shell --basic < /dev/null` + And an empty_session file: + """ + """ + + When I run `wp shell < empty_session` + And I run `wp shell --basic < empty_session` Then STDOUT should be empty Scenario: Persistent environment @@ -252,7 +256,11 @@ Feature: WordPress REPL Scenario: Shell with hook parameter for hook that hasn't fired Given a WP install - When I try `wp shell --basic --hook=shutdown < /dev/null` + And an empty_session file: + """ + """ + + When I try `wp shell --basic --hook=shutdown < empty_session` Then STDERR should contain: """ Error: The 'shutdown' hook has not fired yet