55namespace Revolution \Laravel \Boost ;
66
77use Exception ;
8- use Illuminate \Support \Arr ;
9- use Illuminate \Support \Facades \Process ;
108use Laravel \Boost \Contracts \McpClient ;
119use Laravel \Boost \Install \CodeEnvironment \CodeEnvironment ;
1210use Laravel \Boost \Install \Enums \Platform ;
11+ use Revolution \Laravel \Boost \Concerns \WithWSL ;
1312
1413class PhpStormCopilot extends CodeEnvironment implements McpClient
1514{
15+ use WithWSL;
16+
1617 public bool $ useAbsolutePathForMcp = true ;
1718
1819 public function name (): string
@@ -98,9 +99,7 @@ protected function installFileMcp(string $key, string $command, array $args = []
9899 throw new Exception ('Testbench is not supported. Consider using laravel-boost-copilot-cli instead. ' );
99100 }
100101
101- $ is_wsl = ! empty (getenv ('WSL_DISTRO_NAME ' ));
102-
103- if ($ is_wsl ) {
102+ if ($ this ->isWSL ()) {
104103 return $ this ->installMcpViaWsl ($ key , $ command , $ args );
105104 }
106105
@@ -109,120 +108,6 @@ protected function installFileMcp(string $key, string $command, array $args = []
109108 return parent ::installFileMcp ($ key , $ transformed ['command ' ], $ transformed ['args ' ], $ env );
110109 }
111110
112- protected function installMcpViaWsl (string $ name , string $ command , array $ args ): bool
113- {
114- // Get Windows LOCALAPPDATA path via wslvar command
115- $ localAppDataResult = Process::run ('wslvar LOCALAPPDATA ' );
116- $ localAppData = trim ($ localAppDataResult ->output ());
117-
118- if (empty ($ localAppData )) {
119- return false ;
120- }
121-
122- $ winPath = "{$ localAppData }\\github-copilot \\intellij " ;
123- $ filePath = "{$ winPath }\\mcp.json " ;
124-
125- // Read existing config via PowerShell
126- $ readCommand = "powershell.exe -NoProfile -Command \"if (Test-Path ' {$ filePath }') { Get-Content ' {$ filePath }' -Raw } else { '{}' } \"" ;
127- $ result = Process::run ($ readCommand );
128-
129- $ config = json_decode ($ result ->output () ?: '{} ' , true ) ?: [];
130-
131- if (! isset ($ config [$ this ->mcpConfigKey ()])) {
132- $ config [$ this ->mcpConfigKey ()] = [];
133- }
134-
135- // Transform command and args for PhpStorm WSL compatibility
136- $ transformed = $ this ->transformMcpCommandForWsl ($ command , $ args );
137-
138- $ config [$ this ->mcpConfigKey ()][$ name ] = [
139- 'command ' => $ transformed ['command ' ],
140- 'args ' => $ transformed ['args ' ],
141- ];
142-
143- // Remove empty arrays from existing config to avoid compatibility issues
144- $ config = $ this ->removeEmptyArrays ($ config );
145-
146- $ jsonContent = json_encode ($ config , JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES );
147-
148- // Use Windows TEMP path directly
149- $ tempFileName = 'mcp_ ' .uniqid ().'.json ' ;
150- $ winTempPath = "{$ localAppData }\\Temp \\{$ tempFileName }" ;
151-
152- // Write JSON content to temp file via PowerShell with Base64 encoding
153- $ base64Content = base64_encode ($ jsonContent );
154- $ writeTempCommand = 'powershell.exe -NoProfile -Command " '
155- ."[System.IO.File]::WriteAllBytes(' {$ winTempPath }', [System.Convert]::FromBase64String(' {$ base64Content }')) \"" ;
156-
157- $ writeTempResult = Process::run ($ writeTempCommand );
158-
159- if (! $ writeTempResult ->successful ()) {
160- return false ;
161- }
162-
163- // Create directory and copy file via PowerShell
164- $ copyCommand = 'powershell.exe -NoProfile -Command " '
165- ."New-Item -ItemType Directory -Path ' {$ winPath }' -Force | Out-Null; "
166- ."Copy-Item -Path ' {$ winTempPath }' -Destination ' {$ filePath }' -Force; "
167- ."Remove-Item -Path ' {$ winTempPath }' -Force \"" ;
168-
169- $ copyResult = Process::run ($ copyCommand );
170-
171- return $ copyResult ->successful ();
172- }
173-
174- /**
175- * Transform MCP command and args to PhpStorm-compatible WSL format.
176- *
177- * @param string $command The command from installMcpViaWsl (e.g., 'wsl', './vendor/bin/sail', or absolute sail path)
178- * @param array $args The arguments array
179- * @return array{command: string, args: array} The transformed config for PhpStorm
180- */
181- public function transformMcpCommandForWsl (string $ command , array $ args ): array
182- {
183- // Case 1: Sail is being used (command is ./vendor/bin/sail or absolute path to sail)
184- if (str_ends_with ($ command , '/vendor/bin/sail ' ) || str_ends_with ($ command , '\\vendor \\bin \\sail ' )) {
185- // Expected args: ["artisan", "boost:mcp"]
186- // Transform to: wsl.exe --cd /absolute/path ./vendor/bin/sail artisan boost:mcp
187- $ projectPath = base_path ();
188-
189- return [
190- 'command ' => 'wsl.exe ' ,
191- 'args ' => [
192- '--cd ' ,
193- $ projectPath ,
194- './vendor/bin/sail ' ,
195- ...$ args ,
196- ],
197- ];
198- }
199-
200- // Case 2: WSL without Sail (command is already 'wsl.exe')
201- if (str_starts_with ($ command , 'wsl ' )) {
202- // Args are already in correct format: [php_path, artisan_path, "boost:mcp"]
203- // No transformation needed
204- return [
205- 'command ' => $ command ,
206- 'args ' => $ args ,
207- ];
208- }
209-
210- // Case 3: Future-proof - direct PHP path (absolute or relative)
211- // This might happen if boost changes its behavior in the future
212- // Transform to: wsl.exe --cd /absolute/path {command} {args}
213- $ projectPath = base_path ();
214-
215- return [
216- 'command ' => 'wsl.exe ' ,
217- 'args ' => [
218- '--cd ' ,
219- $ projectPath ,
220- $ command ,
221- ...$ args ,
222- ],
223- ];
224- }
225-
226111 /**
227112 * Transform Sail command to use bash -c wrapper for proper working directory.
228113 *
0 commit comments