99use Illuminate \Support \Collection ;
1010use Illuminate \Support \Facades \Artisan ;
1111use Illuminate \Support \Str ;
12+ use Laravel \Boost \Contracts \Agent ;
13+ use Laravel \Boost \Contracts \Ide ;
1214use Laravel \Boost \Install \Cli \DisplayHelper ;
1315use Laravel \Boost \Install \CodeEnvironmentsDetector ;
1416use Laravel \Boost \Install \GuidelineComposer ;
2527use function Laravel \Prompts \multiselect ;
2628use function Laravel \Prompts \note ;
2729use function Laravel \Prompts \select ;
28- use function Laravel \Prompts \text ;
2930
3031#[AsCommand('boost:install ' , 'Install Laravel Boost ' )]
3132class InstallCommand extends Command
@@ -40,27 +41,24 @@ class InstallCommand extends Command
4041
4142 private Terminal $ terminal ;
4243
43- /** @var Collection<int, \Laravel\Boost\Contracts\ Agent> */
44- private Collection $ agentsToInstallTo ;
44+ /** @var Collection<int, Agent> */
45+ private Collection $ selectedTargetAgents ;
4546
46- /** @var Collection<int, \Laravel\Boost\Contracts\ Ide> */
47- private Collection $ idesToInstallTo ;
47+ /** @var Collection<int, Ide> */
48+ private Collection $ selectedTargetIdes ;
4849
49- private Collection $ boostToInstall ;
50+ /** @var Collection<int, string> */
51+ private Collection $ selectedBoostFeatures ;
5052
5153 private string $ projectName ;
5254
53- private string $ projectPurpose = '' ;
54-
5555 /** @var array<non-empty-string> */
5656 private array $ systemInstalledCodeEnvironments = [];
5757
5858 private array $ projectInstalledCodeEnvironments = [];
5959
6060 private bool $ enforceTests = true ;
6161
62- private array $ boostToolsToDisable = [];
63-
6462 private array $ projectInstalledAgents = [];
6563
6664 private string $ greenTick ;
@@ -73,7 +71,7 @@ public function handle(CodeEnvironmentsDetector $codeEnvironmentsDetector, Herd
7371
7472 $ this ->displayBoostHeader ();
7573 $ this ->discoverEnvironment ();
76- $ this ->query ();
74+ $ this ->collectInstallationPreference ();
7775 $ this ->enact ();
7876 $ this ->outro ();
7977 }
@@ -89,8 +87,8 @@ private function bootstrapBoost(CodeEnvironmentsDetector $codeEnvironmentsDetect
8987 $ this ->greenTick = $ this ->green ('✓ ' );
9088 $ this ->redCross = $ this ->red ('✗ ' );
9189
92- $ this ->agentsToInstallTo = collect ();
93- $ this ->idesToInstallTo = collect ();
90+ $ this ->selectedTargetAgents = collect ();
91+ $ this ->selectedTargetIdes = collect ();
9492
9593 $ this ->projectName = basename (base_path ());
9694 }
@@ -122,28 +120,23 @@ private function discoverEnvironment(): void
122120 $ this ->projectInstalledAgents = $ this ->discoverProjectAgents ();
123121 }
124122
125- private function query ()
123+ private function collectInstallationPreference (): void
126124 {
127- // Which parts of boost should we install
128- $ this ->boostToInstall = $ this ->boostToInstall ();
129- // $this->boostToolsToDisable = $this->boostToolsToDisable(); // Not useful to start
130-
131- // $this->projectPurpose = $this->projectPurpose();
132- $ this ->enforceTests = $ this ->shouldEnforceTests (ask: false );
133-
134- $ this ->idesToInstallTo = $ this ->idesToInstallTo (); // To add boost:mcp to the correct file
135- $ this ->agentsToInstallTo = $ this ->agentsToInstallTo (); // AI Guidelines, which file do they go, are they separated, or all in one file?
125+ $ this ->selectedBoostFeatures = $ this ->selectBoostFeatures ();
126+ $ this ->enforceTests = $ this ->determineTestEnforcement (ask: false );
127+ $ this ->selectedTargetIdes = $ this ->selectTargetIdes ();
128+ $ this ->selectedTargetAgents = $ this ->selectTargetAgents ();
136129 }
137130
138131 private function enact (): void
139132 {
140- if ($ this ->installingGuidelines () && ! empty ($ this ->agentsToInstallTo )) {
133+ if ($ this ->installingGuidelines () && ! empty ($ this ->selectedTargetAgents )) {
141134 $ this ->enactGuidelines ();
142135 }
143136
144137 usleep (750000 );
145138
146- if (($ this ->installingMcp () || $ this ->installingHerdMcp ()) && $ this ->idesToInstallTo ->isNotEmpty ()) {
139+ if (($ this ->installingMcp () || $ this ->installingHerdMcp ()) && $ this ->selectedTargetIdes ->isNotEmpty ()) {
147140 $ this ->enactMcpServers ();
148141 }
149142 }
@@ -176,9 +169,9 @@ private function outro(): void
176169 // Build install data - CSV format with type prefixes
177170 $ data = [];
178171
179- $ ideNames = $ this ->idesToInstallTo ->map (fn ($ ide ) => 'i: ' .class_basename ($ ide ))->toArray ();
180- $ agentNames = $ this ->agentsToInstallTo ->map (fn ($ agent ) => 'a: ' .class_basename ($ agent ))->toArray ();
181- $ boostFeatures = $ this ->boostToInstall ->map (fn ($ feature ) => 'b: ' .$ feature )->toArray ();
172+ $ ideNames = $ this ->selectedTargetIdes ->map (fn ($ ide ) => 'i: ' .class_basename ($ ide ))->toArray ();
173+ $ agentNames = $ this ->selectedTargetAgents ->map (fn ($ agent ) => 'a: ' .class_basename ($ agent ))->toArray ();
174+ $ boostFeatures = $ this ->selectedBoostFeatures ->map (fn ($ feature ) => 'b: ' .$ feature )->toArray ();
182175
183176 // Guidelines installed (prefix: g)
184177 $ guidelines = [];
@@ -210,45 +203,38 @@ private function hyperlink(string $label, string $url): string
210203 return "\033]8;; {$ url }\007{$ label }\033]8;; \033\\" ;
211204 }
212205
213- protected function projectPurpose (): string
214- {
215- return text (
216- label: sprintf ('What does the %s project do? (optional) ' , $ this ->projectName ),
217- placeholder: 'i.e. SaaS platform selling concert tickets, integrates with Stripe and Twilio, lots of CS using Nova backend ' ,
218- default: config ('boost.project_purpose ' ) ?? '' ,
219- hint: 'This helps guides AI. How would you explain it to a new developer? '
220- );
221- }
222-
223206 /**
224207 * We shouldn't add an AI guideline enforcing tests if they don't have a basic test setup.
225- * This would likely just create headaches for them, or be a waste of time as they
208+ * This would likely just create headaches for them or be a waste of time as they
226209 * won't have the CI setup to make use of them anyway, so we're just wasting their
227210 * tokens/money by enforcing them.
211+ *
212+ * @param bool $ask
213+ * @return bool
228214 */
229- protected function shouldEnforceTests (bool $ ask = true ): bool
215+ protected function determineTestEnforcement (bool $ ask = true ): bool
230216 {
231- $ enforce = Finder::create ()
217+ $ hasMinimumTests = Finder::create ()
232218 ->in (base_path ('tests ' ))
233219 ->files ()
234220 ->name ('*.php ' )
235221 ->count () > 6 ;
236222
237- if ($ enforce === false && $ ask === true ) {
238- $ enforce = select (
223+ if (! $ hasMinimumTests && $ ask ) {
224+ $ hasMinimumTests = select (
239225 label: 'Should AI always create tests? ' ,
240226 options: ['Yes ' , 'No ' ],
241227 default: 'Yes '
242228 ) === 'Yes ' ;
243229 }
244230
245- return $ enforce ;
231+ return $ hasMinimumTests ;
246232 }
247233
248234 /**
249235 * @return Collection<int, string>
250236 */
251- protected function boostToInstall (): Collection
237+ private function selectBoostFeatures (): Collection
252238 {
253239 $ defaultToInstallOptions = ['mcp_server ' , 'ai_guidelines ' ];
254240 $ toInstallOptions = [
@@ -316,9 +302,9 @@ private function discoverProjectAgents(): array
316302 }
317303
318304 /**
319- * @return Collection<int, \Laravel\Boost\Contracts\ Ide>
305+ * @return Collection<int, Ide>
320306 */
321- private function idesToInstallTo (): Collection
307+ private function selectTargetIdes (): Collection
322308 {
323309 $ ides = [];
324310 if (! $ this ->installingMcp () && ! $ this ->installingHerdMcp ()) {
@@ -338,7 +324,7 @@ private function idesToInstallTo(): Collection
338324 if (class_exists ($ className )) {
339325 $ reflection = new \ReflectionClass ($ className );
340326
341- if ($ reflection ->implementsInterface (\ Laravel \ Boost \ Contracts \ Ide::class) && ! $ reflection ->isAbstract ()) {
327+ if ($ reflection ->implementsInterface (Ide::class) && ! $ reflection ->isAbstract ()) {
342328 $ ides [$ className ] = Str::headline ($ ideFile ->getBasename ('.php ' ));
343329 }
344330 }
@@ -370,9 +356,9 @@ private function idesToInstallTo(): Collection
370356 }
371357
372358 /**
373- * @return Collection<int, \Laravel\Boost\Contracts\ Agent>
359+ * @return Collection<int, Agent>
374360 */
375- private function agentsToInstallTo (): Collection
361+ private function selectTargetAgents (): Collection
376362 {
377363 $ agents = [];
378364 if (! $ this ->installingGuidelines ()) {
@@ -392,7 +378,7 @@ private function agentsToInstallTo(): Collection
392378 if (class_exists ($ className )) {
393379 $ reflection = new \ReflectionClass ($ className );
394380
395- if ($ reflection ->implementsInterface (\ Laravel \ Boost \ Contracts \ Agent::class)) {
381+ if ($ reflection ->implementsInterface (Agent::class)) {
396382 $ agents [$ className ] = Str::headline ($ agentFile ->getBasename ('.php ' ));
397383 }
398384 }
@@ -427,7 +413,7 @@ protected function enactGuidelines(): void
427413 return ;
428414 }
429415
430- if ($ this ->agentsToInstallTo ->isEmpty ()) {
416+ if ($ this ->selectedTargetAgents ->isEmpty ()) {
431417 $ this ->info ('No agents selected for guideline installation. ' );
432418
433419 return ;
@@ -451,8 +437,8 @@ protected function enactGuidelines(): void
451437 $ failed = [];
452438 $ composedAiGuidelines = $ composer ->compose ();
453439
454- $ longestAgentName = max (1 , ...$ this ->agentsToInstallTo ->map (fn ($ agent ) => Str::length (class_basename ($ agent )))->toArray ());
455- foreach ($ this ->agentsToInstallTo as $ agent ) {
440+ $ longestAgentName = max (1 , ...$ this ->selectedTargetAgents ->map (fn ($ agent ) => Str::length (class_basename ($ agent )))->toArray ());
441+ foreach ($ this ->selectedTargetAgents as $ agent ) {
456442 $ agentName = class_basename ($ agent );
457443 $ displayAgentName = str_pad ($ agentName , $ longestAgentName , ' ' , STR_PAD_RIGHT );
458444 $ this ->output ->write (" {$ displayAgentName }... " );
@@ -483,22 +469,22 @@ protected function enactGuidelines(): void
483469
484470 protected function installingGuidelines (): bool
485471 {
486- return $ this ->boostToInstall ->contains ('ai_guidelines ' );
472+ return $ this ->selectedBoostFeatures ->contains ('ai_guidelines ' );
487473 }
488474
489475 protected function installingStyleGuidelines (): bool
490476 {
491- return $ this ->boostToInstall ->contains ('style_guidelines ' );
477+ return $ this ->selectedBoostFeatures ->contains ('style_guidelines ' );
492478 }
493479
494480 protected function installingMcp (): bool
495481 {
496- return $ this ->boostToInstall ->contains ('mcp_server ' );
482+ return $ this ->selectedBoostFeatures ->contains ('mcp_server ' );
497483 }
498484
499485 protected function installingHerdMcp (): bool
500486 {
501- return $ this ->boostToInstall ->contains ('herd_mcp ' );
487+ return $ this ->selectedBoostFeatures ->contains ('herd_mcp ' );
502488 }
503489
504490 protected function publishAndUpdateConfig (): void
@@ -556,9 +542,9 @@ protected function enactMcpServers(): void
556542 usleep (750000 );
557543
558544 $ failed = [];
559- $ longestIdeName = max (1 , ...$ this ->idesToInstallTo ->map (fn ($ ide ) => Str::length (class_basename ($ ide )))->toArray ());
545+ $ longestIdeName = max (1 , ...$ this ->selectedTargetIdes ->map (fn ($ ide ) => Str::length (class_basename ($ ide )))->toArray ());
560546
561- foreach ($ this ->idesToInstallTo as $ ide ) {
547+ foreach ($ this ->selectedTargetIdes as $ ide ) {
562548 $ ideName = class_basename ($ ide );
563549 $ ideDisplay = str_pad ($ ideName , $ longestIdeName , ' ' , STR_PAD_RIGHT );
564550 $ this ->output ->write (" {$ ideDisplay }... " );
0 commit comments