Skip to content

Commit 21718bc

Browse files
committed
Adjusts things
1 parent 387ea62 commit 21718bc

File tree

9 files changed

+161
-147
lines changed

9 files changed

+161
-147
lines changed

src/Console/InstallCommand.php

Lines changed: 81 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,10 @@
2626
use Symfony\Component\Finder\Finder;
2727
use Symfony\Component\Process\Process;
2828

29+
use function Laravel\Prompts\confirm;
2930
use function Laravel\Prompts\intro;
3031
use function Laravel\Prompts\multiselect;
3132
use function Laravel\Prompts\note;
32-
use function Laravel\Prompts\select;
3333

3434
#[AsCommand('boost:install', 'Install Laravel Boost')]
3535
class InstallCommand extends Command
@@ -134,7 +134,7 @@ protected function collectInstallationPreferences(): void
134134
$this->selectedAiGuidelines = $this->selectAiGuidelines();
135135
$this->selectedTargetMcpClient = $this->selectTargetMcpClients();
136136
$this->selectedTargetAgents = $this->selectTargetAgents();
137-
$this->enforceTests = $this->determineTestEnforcement(ask: false);
137+
$this->enforceTests = $this->determineTestEnforcement();
138138
}
139139

140140
protected function performInstallation(): void
@@ -143,7 +143,7 @@ protected function performInstallation(): void
143143

144144
usleep(750000);
145145

146-
if (($this->shouldInstallMcp() || $this->shouldInstallHerdMcp()) && $this->selectedTargetMcpClient->isNotEmpty()) {
146+
if ($this->selectedTargetMcpClient->isNotEmpty()) {
147147
$this->installMcpServerConfig();
148148
}
149149
}
@@ -179,9 +179,8 @@ protected function outro(): void
179179
$boostFeatures = $this->selectedBoostFeatures->map(fn ($feature): string => 'b:'.$feature)->toArray();
180180

181181
$guidelines = [];
182-
if ($this->shouldInstallAiGuidelines()) {
183-
$guidelines[] = 'g:ai';
184-
}
182+
183+
$guidelines[] = 'g:ai';
185184

186185
if ($this->shouldInstallStyleGuidelines()) {
187186
$guidelines[] = 'g:style';
@@ -210,7 +209,7 @@ protected function hyperlink(string $label, string $url): string
210209
* won't have the CI setup to make use of them anyway, so we're just wasting their
211210
* tokens/money by enforcing them.
212211
*/
213-
protected function determineTestEnforcement(bool $ask = true): bool
212+
protected function determineTestEnforcement(): bool
214213
{
215214
$hasMinimumTests = false;
216215

@@ -226,14 +225,6 @@ protected function determineTestEnforcement(bool $ask = true): bool
226225
->count() >= self::MIN_TEST_COUNT;
227226
}
228227

229-
if (! $hasMinimumTests && $ask) {
230-
return select(
231-
label: 'Should AI always create tests?',
232-
options: ['Yes', 'No'],
233-
default: 'Yes'
234-
) === 'Yes';
235-
}
236-
237228
return $hasMinimumTests;
238229
}
239230

@@ -242,88 +233,61 @@ protected function determineTestEnforcement(bool $ask = true): bool
242233
*/
243234
protected function selectBoostFeatures(): Collection
244235
{
245-
$defaultInstallOptions = [
236+
$features = collect([
246237
'mcp_server',
247-
...$this->config->exists() === false || $this->config->getAiGuidelines() !== [] ? ['ai_guidelines'] : [],
248-
];
238+
'ai_guidelines',
239+
]);
249240

250-
$installOptions = [
251-
'mcp_server' => 'Boost MCP Server (with 15+ tools)',
252-
'ai_guidelines' => 'Boost AI Guidelines (for Laravel, Inertia, and more)',
253-
];
241+
if ($this->herd->isMcpAvailable() === false) {
242+
return $features;
243+
}
254244

255-
if ($this->herd->isMcpAvailable()) {
256-
$installOptions['herd_mcp'] = 'Herd MCP Server';
245+
if (confirm(
246+
label: 'Would you like to install Herd MCP alongside Boost MCP?',
247+
default: $this->config->getHerdMcp(),
248+
hint: 'Herd MCP provides additional tools like browser logs, that can help AI understand issues better',
249+
)) {
250+
$features->push('herd_mcp');
257251
}
258252

259-
return collect(multiselect(
260-
label: 'What do you want to install?',
261-
options: $installOptions,
262-
default: $defaultInstallOptions,
263-
required: true,
264-
));
253+
return $features;
265254
}
266255

267256
/**
268257
* @return Collection<int, string>
269258
*/
270259
protected function selectAiGuidelines(): Collection
271260
{
272-
if (! $this->shouldInstallAiGuidelines()) {
273-
return collect();
274-
}
275-
276-
$aiGuidelines = collect($this->config->getAiGuidelines());
277-
278-
$options = app(GuidelineComposer::class)->guidelines();
279-
$defaults = $aiGuidelines->isNotEmpty()
280-
? $aiGuidelines
281-
: $options->reject(fn (array $guideline) => $guideline['third_party'])->keys();
261+
$options = app(GuidelineComposer::class)->guidelines()
262+
->reject(fn (array $guideline) => $guideline['third_party'] === false);
282263

283264
if ($options->isEmpty()) {
284265
return collect();
285266
}
286267

287268
return collect(multiselect(
288-
label: 'Which AI guidelines do you want to install?',
269+
label: 'Which Third Party AI Guidelines do you want to install?',
289270
// @phpstan-ignore-next-line
290271
options: $options->mapWithKeys(function (array $guideline, string $name) {
291272
$humanName = str_replace('/core', '', $name);
292273

293274
return [$name => "{$humanName} (~{$guideline['tokens']} tokens) {$guideline['description']}"];
294275
}),
295-
default: $defaults,
276+
default: collect($this->config->getGuidelines()),
296277
scroll: 10,
297278
hint: 'You can add or remove them later by running this command again',
298-
required: true,
299279
));
300280
}
301281

302-
/**
303-
* @return array<int, string>
304-
*/
305-
protected function boostToolsToDisable(): array
306-
{
307-
return multiselect(
308-
label: 'Do you need to disable any Boost provided tools?',
309-
options: $this->discoverTools(),
310-
scroll: 4,
311-
hint: 'You can exclude or include them later in the config file',
312-
);
313-
}
314-
315282
/**
316283
* @return Collection<int, CodeEnvironment>
317284
*/
318285
protected function selectTargetMcpClients(): Collection
319286
{
320-
if (! $this->shouldInstallMcp() && ! $this->shouldInstallHerdMcp()) {
321-
return collect();
322-
}
323-
324287
return $this->selectCodeEnvironments(
325288
McpClient::class,
326-
sprintf('Which code editors do you use to work on %s?', $this->projectName)
289+
sprintf('Which code editors do you use to work on %s?', $this->projectName),
290+
$this->config->getEditors(),
327291
);
328292
}
329293

@@ -332,13 +296,10 @@ protected function selectTargetMcpClients(): Collection
332296
*/
333297
protected function selectTargetAgents(): Collection
334298
{
335-
if (! $this->shouldInstallAiGuidelines()) {
336-
return collect();
337-
}
338-
339299
return $this->selectCodeEnvironments(
340300
Agent::class,
341-
sprintf('Which agents need AI guidelines for %s?', $this->projectName)
301+
sprintf('Which agents need AI guidelines for %s?', $this->projectName),
302+
$this->config->getAgents(),
342303
);
343304
}
344305

@@ -357,9 +318,10 @@ protected function getSelectionConfig(string $contractClass): array
357318
}
358319

359320
/**
321+
* @param array<int, string> $defaults
360322
* @return Collection<int, CodeEnvironment>
361323
*/
362-
protected function selectCodeEnvironments(string $contractClass, string $label): Collection
324+
protected function selectCodeEnvironments(string $contractClass, string $label, array $defaults): Collection
363325
{
364326
$allEnvironments = $this->codeEnvironmentsDetector->getCodeEnvironments();
365327
$config = $this->getSelectionConfig($contractClass);
@@ -374,49 +336,48 @@ protected function selectCodeEnvironments(string $contractClass, string $label):
374336
$displayMethod = $config['displayMethod'];
375337
$displayText = $environment->{$displayMethod}();
376338

377-
return [$environment::class => $displayText];
339+
return [$environment->name() => $displayText];
378340
})->sort();
379341

380-
$detectedClasses = [];
381342
$installedEnvNames = array_unique(array_merge(
382343
$this->projectInstalledCodeEnvironments,
383344
$this->systemInstalledCodeEnvironments
384345
));
385346

386-
foreach ($installedEnvNames as $envKey) {
387-
$matchingEnv = $availableEnvironments->first(fn (CodeEnvironment $env): bool => strtolower((string) $envKey) === strtolower($env->name()));
388-
if ($matchingEnv) {
389-
$detectedClasses[] = $matchingEnv::class;
347+
$detectedDefaults = [];
348+
349+
if ($defaults === []) {
350+
foreach ($installedEnvNames as $envKey) {
351+
$matchingEnv = $availableEnvironments->first(fn (CodeEnvironment $env): bool => strtolower((string) $envKey) === strtolower($env->name()));
352+
if ($matchingEnv) {
353+
$detectedDefaults[] = $matchingEnv->name();
354+
}
390355
}
391356
}
392357

393-
$selectedClasses = collect(multiselect(
358+
$selectedCodeEnvironments = collect(multiselect(
394359
label: $label,
395360
options: $options->toArray(),
396-
default: array_unique($detectedClasses),
361+
default: $defaults === [] ? $detectedDefaults : $defaults,
397362
scroll: $config['scroll'],
398363
required: $config['required'],
399-
hint: $detectedClasses === [] ? '' : sprintf('Auto-detected %s for you',
364+
hint: $defaults === [] || $detectedDefaults === [] ? '' : sprintf('Auto-detected %s for you',
400365
Arr::join(array_map(function ($className) use ($availableEnvironments, $config) {
401-
$env = $availableEnvironments->first(fn ($env): bool => $env::class === $className);
366+
$env = $availableEnvironments->first(fn ($env): bool => $env->name() === $className);
402367
$displayMethod = $config['displayMethod'];
403368

404369
return $env->{$displayMethod}();
405-
}, $detectedClasses), ', ', ' & ')
370+
}, $detectedDefaults), ', ', ' & ')
406371
)
407372
))->sort();
408373

409-
return $selectedClasses->map(fn ($className) => $availableEnvironments->first(fn ($env): bool => $env::class === $className));
374+
return $selectedCodeEnvironments->map(
375+
fn (string $name) => $availableEnvironments->first(fn ($env): bool => $env->name() === $name),
376+
)->filter()->values();
410377
}
411378

412379
protected function installGuidelines(): void
413380
{
414-
if (! $this->shouldInstallAiGuidelines()) {
415-
$this->config->setAiGuidelines([]);
416-
417-
return;
418-
}
419-
420381
if ($this->selectedTargetAgents->isEmpty()) {
421382
$this->info(' No agents selected for guideline installation.');
422383

@@ -479,37 +440,35 @@ protected function installGuidelines(): void
479440
}
480441
}
481442

482-
$this->config->setAiGuidelines(
483-
$this->selectedAiGuidelines->values()->toArray()
443+
$this->config->setHerdMcp(
444+
$this->shouldInstallHerdMcp()
484445
);
485-
}
486446

487-
protected function shouldInstallAiGuidelines(): bool
488-
{
489-
return $this->selectedBoostFeatures->contains('ai_guidelines');
447+
$this->config->setEditors(
448+
$this->selectedTargetMcpClient->map(fn (McpClient $mcpClient) => $mcpClient->name())->values()->toArray()
449+
);
450+
451+
$this->config->setAgents(
452+
$this->selectedTargetAgents->map(fn (Agent $agent) => $agent->name())->values()->toArray()
453+
);
454+
455+
$this->config->setGuidelines(
456+
$this->selectedAiGuidelines->values()->toArray()
457+
);
490458
}
491459

492460
protected function shouldInstallStyleGuidelines(): bool
493461
{
494462
return false;
495463
}
496464

497-
protected function shouldInstallMcp(): bool
498-
{
499-
return $this->selectedBoostFeatures->contains('mcp_server');
500-
}
501-
502465
protected function shouldInstallHerdMcp(): bool
503466
{
504467
return $this->selectedBoostFeatures->contains('herd_mcp');
505468
}
506469

507470
protected function installMcpServerConfig(): void
508471
{
509-
if (! $this->shouldInstallMcp() && ! $this->shouldInstallHerdMcp()) {
510-
return;
511-
}
512-
513472
if ($this->selectedTargetMcpClient->isEmpty()) {
514473
$this->info('No agents selected for guideline installation.');
515474

@@ -537,32 +496,30 @@ protected function installMcpServerConfig(): void
537496
$this->output->write(" {$ideDisplay}... ");
538497
$results = [];
539498

540-
if ($this->shouldInstallMcp()) {
541-
$inWsl = $this->isRunningInWsl();
542-
$mcp = array_filter([
543-
'laravel-boost',
544-
$inWsl ? 'wsl' : false,
545-
$mcpClient->getPhpPath($inWsl),
546-
$mcpClient->getArtisanPath($inWsl),
547-
'boost:mcp',
548-
]);
549-
try {
550-
$result = $mcpClient->installMcp(
551-
array_shift($mcp),
552-
array_shift($mcp),
553-
$mcp
554-
);
555-
556-
if ($result) {
557-
$results[] = $this->greenTick.' Boost';
558-
} else {
559-
$results[] = $this->redCross.' Boost';
560-
$failed[$ideName]['boost'] = 'Failed to write configuration';
561-
}
562-
} catch (Exception $e) {
499+
$inWsl = $this->isRunningInWsl();
500+
$mcp = array_filter([
501+
'laravel-boost',
502+
$inWsl ? 'wsl' : false,
503+
$mcpClient->getPhpPath($inWsl),
504+
$mcpClient->getArtisanPath($inWsl),
505+
'boost:mcp',
506+
]);
507+
try {
508+
$result = $mcpClient->installMcp(
509+
array_shift($mcp),
510+
array_shift($mcp),
511+
$mcp
512+
);
513+
514+
if ($result) {
515+
$results[] = $this->greenTick.' Boost';
516+
} else {
563517
$results[] = $this->redCross.' Boost';
564-
$failed[$ideName]['boost'] = $e->getMessage();
518+
$failed[$ideName]['boost'] = 'Failed to write configuration';
565519
}
520+
} catch (Exception $e) {
521+
$results[] = $this->redCross.' Boost';
522+
$failed[$ideName]['boost'] = $e->getMessage();
566523
}
567524

568525
// Install Herd MCP if enabled

src/Contracts/Agent.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
*/
1010
interface Agent
1111
{
12+
public function name(): string;
13+
1214
/**
1315
* Get the display name of the Agent.
1416
*/

src/Contracts/McpClient.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
*/
1010
interface McpClient
1111
{
12+
public function name(): string;
13+
1214
/**
1315
* Get the display name of the MCP (Model Context Protocol) client.
1416
*/

0 commit comments

Comments
 (0)