Skip to content

Commit 0050a3c

Browse files
committed
Add hooks for registering third party code environments
Signed-off-by: Pushpak Chhajed <[email protected]>
1 parent 642a3b6 commit 0050a3c

File tree

10 files changed

+329
-240
lines changed

10 files changed

+329
-240
lines changed

src/Boost.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Laravel\Boost;
6+
7+
use Illuminate\Support\Facades\Facade;
8+
9+
/**
10+
* @method static void registerCodeEnvironment(string $key, string $className)
11+
* @method static array getCodeEnvironments()
12+
*
13+
* @see \Laravel\Boost\BoostManager
14+
*/
15+
class Boost extends Facade
16+
{
17+
protected static function getFacadeAccessor(): string
18+
{
19+
return BoostManager::class;
20+
}
21+
}

src/BoostManager.php

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Laravel\Boost;
6+
7+
use Laravel\Boost\Install\CodeEnvironment\ClaudeCode;
8+
use Laravel\Boost\Install\CodeEnvironment\CodeEnvironment;
9+
use Laravel\Boost\Install\CodeEnvironment\Codex;
10+
use Laravel\Boost\Install\CodeEnvironment\Copilot;
11+
use Laravel\Boost\Install\CodeEnvironment\Cursor;
12+
use Laravel\Boost\Install\CodeEnvironment\PhpStorm;
13+
use Laravel\Boost\Install\CodeEnvironment\VSCode;
14+
15+
class BoostManager
16+
{
17+
/** @var array<string, class-string<CodeEnvironment>> */
18+
private array $codeEnvironments = [
19+
'phpstorm' => PhpStorm::class,
20+
'vscode' => VSCode::class,
21+
'cursor' => Cursor::class,
22+
'claudecode' => ClaudeCode::class,
23+
'codex' => Codex::class,
24+
'copilot' => Copilot::class,
25+
];
26+
27+
/**
28+
* @param class-string<CodeEnvironment> $className
29+
*/
30+
public function registerCodeEnvironment(string $key, string $className): void
31+
{
32+
if (array_key_exists($key, $this->codeEnvironments)) {
33+
return;
34+
}
35+
36+
$this->codeEnvironments[$key] = $className;
37+
}
38+
39+
/**
40+
* @return array<string, class-string<CodeEnvironment>>
41+
*/
42+
public function getCodeEnvironments(): array
43+
{
44+
return $this->codeEnvironments;
45+
}
46+
}

src/BoostServiceProvider.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ public function register(): void
2626
'boost'
2727
);
2828

29+
$this->app->singleton(BoostManager::class, fn (): BoostManager => new BoostManager);
30+
2931
if (! $this->shouldRun()) {
3032
return;
3133
}

src/Install/CodeEnvironment/CodeEnvironment.php

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
namespace Laravel\Boost\Install\CodeEnvironment;
66

7-
use Illuminate\Contracts\Filesystem\FileNotFoundException;
87
use Illuminate\Support\Facades\Process;
98
use Laravel\Boost\Contracts\Agent;
109
use Laravel\Boost\Contracts\McpClient;
@@ -118,8 +117,6 @@ public function mcpConfigKey(): string
118117
*
119118
* @param array<int, string> $args
120119
* @param array<string, string> $env
121-
*
122-
* @throws FileNotFoundException
123120
*/
124121
public function installMcp(string $key, string $command, array $args = [], array $env = []): bool
125122
{
@@ -176,8 +173,6 @@ protected function installShellMcp(string $key, string $command, array $args = [
176173
*
177174
* @param array<int, string> $args
178175
* @param array<string, string> $env
179-
*
180-
* @throws FileNotFoundException
181176
*/
182177
protected function installFileMcp(string $key, string $command, array $args = [], array $env = []): bool
183178
{

src/Install/CodeEnvironmentsDetector.php

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,29 +6,15 @@
66

77
use Illuminate\Container\Container;
88
use Illuminate\Support\Collection;
9-
use Laravel\Boost\Install\CodeEnvironment\ClaudeCode;
9+
use Laravel\Boost\BoostManager;
1010
use Laravel\Boost\Install\CodeEnvironment\CodeEnvironment;
11-
use Laravel\Boost\Install\CodeEnvironment\Codex;
12-
use Laravel\Boost\Install\CodeEnvironment\Copilot;
13-
use Laravel\Boost\Install\CodeEnvironment\Cursor;
14-
use Laravel\Boost\Install\CodeEnvironment\PhpStorm;
15-
use Laravel\Boost\Install\CodeEnvironment\VSCode;
1611
use Laravel\Boost\Install\Enums\Platform;
1712

1813
class CodeEnvironmentsDetector
1914
{
20-
/** @var array<string, class-string<CodeEnvironment>> */
21-
private array $programs = [
22-
'phpstorm' => PhpStorm::class,
23-
'vscode' => VSCode::class,
24-
'cursor' => Cursor::class,
25-
'claudecode' => ClaudeCode::class,
26-
'codex' => Codex::class,
27-
'copilot' => Copilot::class,
28-
];
29-
3015
public function __construct(
31-
private readonly Container $container
16+
private readonly Container $container,
17+
private readonly BoostManager $boostManager
3218
) {}
3319

3420
/**
@@ -68,6 +54,7 @@ public function discoverProjectInstalledCodeEnvironments(string $basePath): arra
6854
*/
6955
public function getCodeEnvironments(): Collection
7056
{
71-
return collect($this->programs)->map(fn (string $className) => $this->container->make($className));
57+
return collect($this->boostManager->getCodeEnvironments())
58+
->map(fn (string $className) => $this->container->make($className));
7259
}
7360
}

tests/Feature/BoostFacadeTest.php

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use Laravel\Boost\Boost;
6+
use Laravel\Boost\BoostManager;
7+
use Tests\Unit\Install\ExampleCodeEnvironment;
8+
9+
it('Boost Facade resolves to BoostManager instance', function (): void {
10+
$instance = Boost::getFacadeRoot();
11+
12+
expect($instance)->toBeInstanceOf(BoostManager::class);
13+
});
14+
15+
it('Boost Facade registers code environments via facade', function (): void {
16+
Boost::registerCodeEnvironment('example1', ExampleCodeEnvironment::class);
17+
Boost::registerCodeEnvironment('example2', ExampleCodeEnvironment::class);
18+
$registered = Boost::getFacadeRoot()->getCodeEnvironments();
19+
20+
expect($registered)->toHaveKey('example1')
21+
->and($registered['example1'])->toBe(ExampleCodeEnvironment::class)
22+
->and($registered)->toHaveKey('example2')
23+
->and($registered['example2'])->toBe(ExampleCodeEnvironment::class)
24+
->and($registered)->toHaveKey('phpstorm');
25+
});
26+
27+
it('Boost Facade maintains registration state across facade calls', function (): void {
28+
Boost::registerCodeEnvironment('persistent', 'Test\Persistent');
29+
30+
$registered = Boost::getFacadeRoot()->getCodeEnvironments();
31+
32+
expect($registered)->toHaveKey('persistent')
33+
->and($registered['persistent'])->toBe('Test\Persistent');
34+
});

tests/Feature/BoostServiceProviderTest.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
declare(strict_types=1);
44

55
use Illuminate\Support\Facades\Config;
6+
use Laravel\Boost\Boost;
7+
use Laravel\Boost\BoostManager;
68
use Laravel\Boost\BoostServiceProvider;
79

810
beforeEach(function (): void {
@@ -74,3 +76,24 @@
7476
});
7577
});
7678
});
79+
80+
describe('BoostManager registration', function (): void {
81+
it('registers BoostManager in the container', function (): void {
82+
expect(app()->bound(BoostManager::class))->toBeTrue()
83+
->and(app(BoostManager::class))->toBeInstanceOf(BoostManager::class);
84+
});
85+
86+
it('registers BoostManager as a singleton', function (): void {
87+
$instance1 = app(BoostManager::class);
88+
$instance2 = app(BoostManager::class);
89+
90+
expect($instance1)->toBe($instance2);
91+
});
92+
93+
it('binds Boost facade to the same BoostManager instance', function (): void {
94+
$containerInstance = app(BoostManager::class);
95+
$facadeInstance = Boost::getFacadeRoot();
96+
97+
expect($facadeInstance)->toBe($containerInstance);
98+
});
99+
});

tests/Unit/BoostManagerTest.php

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use Laravel\Boost\BoostManager;
6+
use Laravel\Boost\Install\CodeEnvironment\ClaudeCode;
7+
use Laravel\Boost\Install\CodeEnvironment\Codex;
8+
use Laravel\Boost\Install\CodeEnvironment\Copilot;
9+
use Laravel\Boost\Install\CodeEnvironment\Cursor;
10+
use Laravel\Boost\Install\CodeEnvironment\PhpStorm;
11+
use Laravel\Boost\Install\CodeEnvironment\VSCode;
12+
use Tests\Unit\Install\ExampleCodeEnvironment;
13+
14+
it('return default code environments', function (): void {
15+
$manager = new BoostManager;
16+
$registered = $manager->getCodeEnvironments();
17+
18+
expect($registered)->toMatchArray([
19+
'phpstorm' => PhpStorm::class,
20+
'vscode' => VSCode::class,
21+
'cursor' => Cursor::class,
22+
'claudecode' => ClaudeCode::class,
23+
'codex' => Codex::class,
24+
'copilot' => Copilot::class,
25+
]);
26+
});
27+
28+
it('can register a single code environment', function (): void {
29+
$manager = new BoostManager;
30+
$manager->registerCodeEnvironment('example', ExampleCodeEnvironment::class);
31+
32+
$registered = $manager->getCodeEnvironments();
33+
34+
expect($registered)->toHaveKey('example')
35+
->and($registered['example'])->toBe(ExampleCodeEnvironment::class)
36+
->and($registered)->toHaveKey('phpstorm');
37+
});
38+
39+
it('can register multiple code environments', function (): void {
40+
$manager = new BoostManager;
41+
$manager->registerCodeEnvironment('example1', ExampleCodeEnvironment::class);
42+
$manager->registerCodeEnvironment('example2', ExampleCodeEnvironment::class);
43+
44+
$registered = $manager->getCodeEnvironments();
45+
46+
expect($registered)->toHaveKey('example1')->toHaveKey('example2')
47+
->and($registered['example1'])->toBe(ExampleCodeEnvironment::class)
48+
->and($registered['example2'])->toBe(ExampleCodeEnvironment::class)
49+
->and($registered)->toHaveKey('phpstorm');
50+
});
51+
52+
it('does not overwrite default code environments', function (): void {
53+
$manager = new BoostManager;
54+
$manager->registerCodeEnvironment('phpstorm', PHPStorm::class);
55+
$manager->registerCodeEnvironment('phpstorm', ExampleCodeEnvironment::class);
56+
57+
$registered = $manager->getCodeEnvironments();
58+
59+
expect($registered)->toHaveKey('phpstorm')
60+
->and($registered['phpstorm'])->toBe(PHPStorm::class)
61+
->and($registered['phpstorm'])->not()->toBe(ExampleCodeEnvironment::class);
62+
});

0 commit comments

Comments
 (0)