Skip to content

Commit c8af128

Browse files
committed
Add WSL command transformation for PhpStorm MCP
Introduces transformMcpCommandForWsl to standardize MCP command and arguments for PhpStorm compatibility with WSL and Sail. Updates installMcpViaWsl to use the transformation. Adds comprehensive tests for various command scenarios. Updates documentation and requirements to Laravel Boost 1.7 and notes Sail support.
1 parent 7af125d commit c8af128

File tree

5 files changed

+173
-6
lines changed

5 files changed

+173
-6
lines changed

.github/copilot-instructions.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ This is a Laravel package that provides custom CodeEnvironment integration for P
88

99
- **Language**: PHP 8.3+
1010
- **Framework**: Laravel 12.x+
11-
- **Dependencies**: Laravel Boost 1.6+
11+
- **Dependencies**: Laravel Boost 1.7+
1212
- **Target Platforms**: macOS, Linux, Windows (Native Windows supported)
1313
- **Testing**: Pest PHP 4.x
1414
- **Code Quality**: Laravel Pint (PSR-12)

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,15 @@
77
## Requirements
88
- PHP >= 8.3
99
- Laravel >= 12.x
10-
- [Laravel Boost](https://github.com/laravel/boost) >= 1.6
10+
- [Laravel Boost](https://github.com/laravel/boost) >= 1.7
1111
- [GitHub Copilot plugin](https://plugins.jetbrains.com/plugin/17718-github-copilot) installed in PhpStorm
1212

1313
## Supported OS
1414
- macOS
1515
- Windows (Native Windows)
1616
- Linux
1717

18-
> **Note**: Laravel Sail is not yet supported.
18+
> **Note**: It also supports Laravel Sail. Before use, start it with `vendor/bin/sail up -d`.
1919
2020
### WSL (Windows Subsystem for Linux)
2121

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
"require": {
1313
"php": "^8.3",
1414
"illuminate/support": "^12.30",
15-
"laravel/boost": "^1.6"
15+
"laravel/boost": "^1.7"
1616
},
1717
"require-dev": {
1818
"laravel/pint": "^1.25",

src/PhpStormCopilot.php

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,58 @@ protected function installFileMcp(string $key, string $command, array $args = []
9898
return parent::installFileMcp($key, $command, $args, $env);
9999
}
100100

101+
/**
102+
* Transform MCP command and args to PhpStorm-compatible WSL format.
103+
*
104+
* @param string $command The command from installMcpViaWsl (e.g., 'wsl', './vendor/bin/sail', or absolute sail path)
105+
* @param array $args The arguments array
106+
* @return array{command: string, args: array} The transformed config for PhpStorm
107+
*/
108+
public function transformMcpCommandForWsl(string $command, array $args): array
109+
{
110+
// Case 1: Sail is being used (command is ./vendor/bin/sail or absolute path to sail)
111+
if (str_ends_with($command, '/vendor/bin/sail') || str_ends_with($command, '\\vendor\\bin\\sail')) {
112+
// Expected args: ["artisan", "boost:mcp"]
113+
// Transform to: wsl --cd /absolute/path ./vendor/bin/sail artisan boost:mcp
114+
$projectPath = base_path();
115+
116+
return [
117+
'command' => 'wsl',
118+
'args' => [
119+
'--cd',
120+
$projectPath,
121+
'./vendor/bin/sail',
122+
...$args,
123+
],
124+
];
125+
}
126+
127+
// Case 2: WSL without Sail (command is already 'wsl')
128+
if ($command === 'wsl') {
129+
// Args are already in correct format: [php_path, artisan_path, "boost:mcp"]
130+
// No transformation needed
131+
return [
132+
'command' => $command,
133+
'args' => $args,
134+
];
135+
}
136+
137+
// Case 3: Future-proof - direct PHP path (absolute or relative)
138+
// This might happen if boost changes its behavior in the future
139+
// Transform to: wsl --cd /absolute/path {command} {args}
140+
$projectPath = base_path();
141+
142+
return [
143+
'command' => 'wsl',
144+
'args' => [
145+
'--cd',
146+
$projectPath,
147+
$command,
148+
...$args,
149+
],
150+
];
151+
}
152+
101153
protected function installMcpViaWsl(string $name, string $command, array $args): bool
102154
{
103155
// Get Windows username via wslvar command
@@ -121,9 +173,12 @@ protected function installMcpViaWsl(string $name, string $command, array $args):
121173
$config[$this->mcpConfigKey()] = [];
122174
}
123175

176+
// Transform command and args for PhpStorm WSL compatibility
177+
$transformed = $this->transformMcpCommandForWsl($command, $args);
178+
124179
$config[$this->mcpConfigKey()][$name] = [
125-
'command' => $command,
126-
'args' => $args,
180+
'command' => $transformed['command'],
181+
'args' => $transformed['args'],
127182
];
128183

129184
// Remove empty arrays from existing config to avoid compatibility issues

tests/Feature/PhpStormCopilotTest.php

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,3 +88,115 @@
8888

8989
expect($phpStormCopilot->getArtisanPath(false))->toBe($artisanPath);
9090
});
91+
92+
test('transformMcpCommandForWsl handles Sail with relative path', function (): void {
93+
$strategyFactory = Mockery::mock(DetectionStrategyFactory::class);
94+
$phpStormCopilot = new PhpStormCopilot($strategyFactory);
95+
96+
$command = './vendor/bin/sail';
97+
$args = ['artisan', 'boost:mcp'];
98+
99+
$result = $phpStormCopilot->transformMcpCommandForWsl($command, $args);
100+
101+
expect($result['command'])->toBe('wsl')
102+
->and($result['args'])->toBe([
103+
'--cd',
104+
base_path(),
105+
'./vendor/bin/sail',
106+
'artisan',
107+
'boost:mcp',
108+
]);
109+
});
110+
111+
test('transformMcpCommandForWsl handles Sail with absolute path', function (): void {
112+
$strategyFactory = Mockery::mock(DetectionStrategyFactory::class);
113+
$phpStormCopilot = new PhpStormCopilot($strategyFactory);
114+
115+
$command = '/home/user/project/vendor/bin/sail';
116+
$args = ['artisan', 'boost:mcp'];
117+
118+
$result = $phpStormCopilot->transformMcpCommandForWsl($command, $args);
119+
120+
expect($result['command'])->toBe('wsl')
121+
->and($result['args'])->toBe([
122+
'--cd',
123+
base_path(),
124+
'./vendor/bin/sail',
125+
'artisan',
126+
'boost:mcp',
127+
]);
128+
});
129+
130+
test('transformMcpCommandForWsl handles Sail with Windows-style path', function (): void {
131+
$strategyFactory = Mockery::mock(DetectionStrategyFactory::class);
132+
$phpStormCopilot = new PhpStormCopilot($strategyFactory);
133+
134+
$command = 'C:\\Users\\user\\project\\vendor\\bin\\sail';
135+
$args = ['artisan', 'boost:mcp'];
136+
137+
$result = $phpStormCopilot->transformMcpCommandForWsl($command, $args);
138+
139+
expect($result['command'])->toBe('wsl')
140+
->and($result['args'])->toBe([
141+
'--cd',
142+
base_path(),
143+
'./vendor/bin/sail',
144+
'artisan',
145+
'boost:mcp',
146+
]);
147+
});
148+
149+
test('transformMcpCommandForWsl handles WSL without Sail', function (): void {
150+
$strategyFactory = Mockery::mock(DetectionStrategyFactory::class);
151+
$phpStormCopilot = new PhpStormCopilot($strategyFactory);
152+
153+
$command = 'wsl';
154+
$args = ['/usr/bin/php', '/home/user/project/artisan', 'boost:mcp'];
155+
156+
$result = $phpStormCopilot->transformMcpCommandForWsl($command, $args);
157+
158+
expect($result['command'])->toBe('wsl')
159+
->and($result['args'])->toBe([
160+
'/usr/bin/php',
161+
'/home/user/project/artisan',
162+
'boost:mcp',
163+
]);
164+
});
165+
166+
test('transformMcpCommandForWsl handles direct PHP path', function (): void {
167+
$strategyFactory = Mockery::mock(DetectionStrategyFactory::class);
168+
$phpStormCopilot = new PhpStormCopilot($strategyFactory);
169+
170+
$command = '/usr/bin/php';
171+
$args = ['/home/user/project/artisan', 'boost:mcp'];
172+
173+
$result = $phpStormCopilot->transformMcpCommandForWsl($command, $args);
174+
175+
expect($result['command'])->toBe('wsl')
176+
->and($result['args'])->toBe([
177+
'--cd',
178+
base_path(),
179+
'/usr/bin/php',
180+
'/home/user/project/artisan',
181+
'boost:mcp',
182+
]);
183+
});
184+
185+
test('transformMcpCommandForWsl handles relative PHP path', function (): void {
186+
$strategyFactory = Mockery::mock(DetectionStrategyFactory::class);
187+
$phpStormCopilot = new PhpStormCopilot($strategyFactory);
188+
189+
$command = 'php';
190+
$args = ['artisan', 'boost:mcp'];
191+
192+
$result = $phpStormCopilot->transformMcpCommandForWsl($command, $args);
193+
194+
expect($result['command'])->toBe('wsl')
195+
->and($result['args'])->toBe([
196+
'--cd',
197+
base_path(),
198+
'php',
199+
'artisan',
200+
'boost:mcp',
201+
]);
202+
});

0 commit comments

Comments
 (0)