Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 64 additions & 20 deletions src/Console/InstallCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -235,26 +235,37 @@ protected function determineTestEnforcement(): bool
*/
protected function selectBoostFeatures(): Collection
{
$features = collect([
'mcp_server',
'ai_guidelines',
]);
$features = collect(['mcp_server', 'ai_guidelines']);

if ($this->herd->isMcpAvailable() === false) {
return $features;
if ($this->herd->isMcpAvailable() && $this->shouldConfigureHerdMcp()) {
$features->push('herd_mcp');
}

if (confirm(
label: 'Would you like to install Herd MCP alongside Boost MCP?',
default: $this->config->getHerdMcp(),
hint: 'The Herd MCP provides additional tools like browser logs, which can help AI understand issues better',
)) {
$features->push('herd_mcp');
if ($this->isSailInstalled() && ($this->isRunningInsideSail() || $this->shouldConfigureSail())) {
$features->push('sail');
}

return $features;
}

protected function shouldConfigureSail(): bool
{
return confirm(
label: 'Laravel Sail detected. Configure Boost MCP to use Sail?',
default: $this->config->getSail(),
hint: 'This will configure the MCP server to run through Sail. Note: Sail must be running to use Boost MCP',
);
}

protected function shouldConfigureHerdMcp(): bool
{
return confirm(
label: 'Would you like to install Herd MCP alongside Boost MCP?',
default: $this->config->getHerdMcp(),
hint: 'The Herd MCP provides additional tools like browser logs, which can help AI understand issues better',
);
}

/**
* @return Collection<int, string>
*/
Expand Down Expand Up @@ -442,6 +453,10 @@ protected function installGuidelines(): void
}
}

$this->config->setSail(
$this->shouldUseSail()
);

$this->config->setHerdMcp(
$this->shouldInstallHerdMcp()
);
Expand Down Expand Up @@ -469,6 +484,39 @@ protected function shouldInstallHerdMcp(): bool
return $this->selectedBoostFeatures->contains('herd_mcp');
}

protected function shouldUseSail(): bool
{
return $this->selectedBoostFeatures->contains('sail');
}

protected function isSailInstalled(): bool
{
return file_exists(base_path('vendor/bin/sail')) &&
(file_exists(base_path('docker-compose.yml')) || file_exists(base_path('compose.yaml')));
}

protected function isRunningInsideSail(): bool
{
return get_current_user() === 'sail' || getenv('LARAVEL_SAIL') === '1';
}

protected function buildMcpCommand(McpClient $mcpClient): array
{
if ($this->shouldUseSail()) {
return ['laravel-boost', './vendor/bin/sail', 'artisan', 'boost:mcp'];
}

$inWsl = $this->isRunningInWsl();

return array_filter([
'laravel-boost',
$inWsl ? 'wsl' : false,
$mcpClient->getPhpPath($inWsl),
$mcpClient->getArtisanPath($inWsl),
'boost:mcp',
]);
}

protected function installMcpServerConfig(): void
{
if ($this->selectedTargetMcpClient->isEmpty()) {
Expand Down Expand Up @@ -498,14 +546,8 @@ protected function installMcpServerConfig(): void
$this->output->write(" {$ideDisplay}... ");
$results = [];

$inWsl = $this->isRunningInWsl();
$mcp = array_filter([
'laravel-boost',
$inWsl ? 'wsl' : false,
$mcpClient->getPhpPath($inWsl),
$mcpClient->getArtisanPath($inWsl),
'boost:mcp',
]);
$mcp = $this->buildMcpCommand($mcpClient);

try {
$result = $mcpClient->installMcp(
array_shift($mcp),
Expand All @@ -527,6 +569,7 @@ protected function installMcpServerConfig(): void
// Install Herd MCP if enabled
if ($this->shouldInstallHerdMcp()) {
$php = $mcpClient->getPhpPath();

try {
$result = $mcpClient->installMcp(
key: 'herd',
Expand Down Expand Up @@ -554,6 +597,7 @@ protected function installMcpServerConfig(): void

if ($failed !== []) {
$this->error(sprintf('%s Some MCP servers failed to install:', $this->redCross));

foreach ($failed as $ideName => $errors) {
foreach ($errors as $server => $error) {
$this->line(" - {$ideName} ({$server}): {$error}");
Expand Down
4 changes: 2 additions & 2 deletions src/Support/Composer.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public static function packagesDirectories(): array
base_path('vendor'),
str_replace('/', DIRECTORY_SEPARATOR, $package),
])])
->filter(is_dir(...))
->filter(fn (string $path): bool => is_dir($path))
->toArray();
}

Expand Down Expand Up @@ -45,7 +45,7 @@ public static function packagesDirectoriesWithBoostGuidelines(): array
'resources',
'boost',
'guidelines',
]))->filter(is_dir(...))
]))->filter(fn (string $path): bool => is_dir($path))
->toArray();
}
}
10 changes: 10 additions & 0 deletions src/Support/Config.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,16 @@ public function getHerdMcp(): bool
return $this->get('herd_mcp', false);
}

public function setSail(bool $useSail): void
{
$this->set('sail', $useSail);
}

public function getSail(): bool
{
return $this->get('sail', false);
}

public function flush(): void
{
$path = base_path(self::FILE);
Expand Down
2 changes: 1 addition & 1 deletion tests/Feature/BoostServiceProviderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

beforeEach(function (): void {
$this->refreshApplication();
Config::set('logging.channels.browser', null);
Config::set('logging.channels.browser');
});

describe('boost.enabled configuration', function (): void {
Expand Down
4 changes: 2 additions & 2 deletions tests/Feature/Install/GuidelineComposerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -407,8 +407,8 @@
->toContain('`artisan make:model`')
->toContain('`php artisan migrate`')
->toContain('`Model::query()`')
->toContain('`route(\'home\')`')
->toContain('`config(\'app.name\')`')
->toContain("`route('home')`")
->toContain("`config('app.name')`")
// Preserves PHP tags in blade templates
->toContain('=== .ai/test-blade-with-php-tags rules ===')
->not->toContain('=== .ai/test-blade-with-backticks.blade.php rules ===')
Expand Down
2 changes: 1 addition & 1 deletion tests/Feature/Mcp/Tools/ListAvailableConfigKeysTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@

test('it handles empty config gracefully', function (): void {
// Clear all config
config()->set('test', null);
config()->set('test');

$tool = new ListAvailableConfigKeys;
$response = $tool->handle(new Request([]));
Expand Down
10 changes: 10 additions & 0 deletions tests/Unit/Install/CodeEnvironment/CodeEnvironmentTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -415,3 +415,13 @@ public function mcpConfigPath(): string
$environment = new TestCodeEnvironment($this->strategyFactory);
expect($environment->getPhpPath(false))->toBe('php');
});

test('getArtisanPath uses absolute path when forceAbsolutePath is true', function (): void {
$environment = new TestCodeEnvironment($this->strategyFactory);
expect($environment->getArtisanPath(true))->toBe(base_path('artisan'));
});

test('getArtisanPath maintains default behavior when forceAbsolutePath is false', function (): void {
$environment = new TestCodeEnvironment($this->strategyFactory);
expect($environment->getArtisanPath(false))->toBe('artisan');
});
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@
$this->firstStrategy,
]);

$result = $composite->detect(['config' => 'test'], null);
$result = $composite->detect(['config' => 'test']);

expect($result)->toBeTrue();
});
Expand Down