Skip to content

Commit e0c2555

Browse files
committed
feat: add an option to opt-out of agentic ai
1 parent d74ec32 commit e0c2555

8 files changed

Lines changed: 164 additions & 7 deletions

File tree

.aion/Choices/Enums/ConfigKeyEnum.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,5 @@ enum ConfigKeyEnum: string
1212
case DB = 'db';
1313
case PhpStan = 'phpstan';
1414
case Frontend = 'frontend';
15+
case AgenticAi = 'agentic-ai';
1516
}

.aion/Commands/AionSandboxCommand.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ private function getProfiles(): array
233233
'web-stateful' => ['--frontend', '--api-type=stateful', '--logging-structure=default', '--oauth', '--oauth-providers=google'],
234234
'api-stateless-full' => ['--no-frontend', '--api-type=stateless', '--db=mysql,pgsql', '--logging-structure=ecs', '--phpstan=9', '--oauth', '--oauth-providers=google,github,apple'],
235235
'api-stateful-lite' => ['--no-frontend', '--api-type=stateful', '--db=sqlite', '--logging-structure=default', '--logs=stderr', '--phpstan=5'],
236-
'oauth-full' => ['--no-frontend', '--api-type=stateless', '--oauth', '--oauth-providers=google,github,apple'],
236+
'oauth-full-no-ai' => ['--no-frontend', '--api-type=stateless', '--oauth', '--oauth-providers=google,github,apple', '--no-agentic-ai'],
237237
];
238238
}
239239
}

.aion/Commands/AionSparkCommand.php

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use Aion\Features\Authentication\ApiTokensFeature;
1313
use Aion\Features\Authentication\OAuthFeature;
1414
use Aion\Features\ExternalTools\ECSLoggingFeature;
15+
use Aion\Features\System\AgenticAiSystemFeature;
1516
use Aion\Features\System\DatabaseSystemFeature;
1617
use Aion\Features\System\LogSystemFeature;
1718
use Aion\Features\System\PhpStanSystemFeature;
@@ -70,6 +71,7 @@ private function bootstrapFeatureRegistry(): void
7071
LogSystemFeature::class,
7172
DatabaseSystemFeature::class,
7273
PhpStanSystemFeature::class,
74+
AgenticAiSystemFeature::class,
7375
]);
7476
}
7577

@@ -108,7 +110,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
108110
);
109111

110112
if (! $input->getOption('dry-run')) {
111-
$this->installDependencies($output);
113+
$this->installDependencies($output, $aionConfig);
112114
}
113115

114116
info('>_ ✨ Setup is complete. Enjoy your new Aion-backed project!'.($input->getOption('dry-run') ? ' (Dry Run Complete)' : ''));
@@ -199,7 +201,7 @@ private function runEngine(
199201
$output->writeln('');
200202
}
201203

202-
private function installDependencies(OutputInterface $output): void
204+
private function installDependencies(OutputInterface $output, AionConfig $config): void
203205
{
204206
if ($this->isInteractive && ! confirm('Would you like to install the composer dependencies now?')) {
205207
return;
@@ -212,7 +214,7 @@ private function installDependencies(OutputInterface $output): void
212214
passthru('composer install --no-scripts');
213215
passthru('php artisan key:generate');
214216

215-
if ($this->isInteractive) {
217+
if ($this->isInteractive && $config->bool(ConfigKeyEnum::AgenticAi, true)) {
216218
passthru('php artisan boost:install');
217219
}
218220

.aion/Commands/Prompters/ConfigurationPrompter.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,9 @@ private function handleConfirmation(
8585
bool $default = false,
8686
?string $hint = null,
8787
): bool {
88-
if ($predefined = $this->getPredefinedValue($option)) {
88+
$predefined = $this->getPredefinedValue($option);
89+
90+
if ($predefined !== null) {
8991
info("> Using pre-configured $displayName: ".($predefined === '1' || $predefined === true ? 'Yes' : 'No'));
9092

9193
return filter_var($predefined, FILTER_VALIDATE_BOOLEAN);
@@ -109,7 +111,9 @@ private function handleSelection(
109111
bool $isMulti = false,
110112
?string $hint = null
111113
): mixed {
112-
if ($predefined = $this->getPredefinedValue($option)) {
114+
$predefined = $this->getPredefinedValue($option);
115+
116+
if ($predefined !== null) {
113117
$parsed = $isMulti ? explode(',', (string) $predefined) : $predefined;
114118
$display = $isMulti ? implode(', ', (array) $parsed) : (string) $parsed;
115119

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php
2+
3+
namespace Aion\Engine\Operations;
4+
5+
use League\Flysystem\FilesystemOperator;
6+
7+
class RemoveComposerDependencyOperation implements OperationContract
8+
{
9+
public function __construct(
10+
private readonly string $packageName,
11+
private readonly bool $dev = false
12+
) {}
13+
14+
public function execute(FilesystemOperator $filesystem): void
15+
{
16+
$composerPath = 'composer.json';
17+
18+
if (! $filesystem->fileExists($composerPath)) {
19+
return;
20+
}
21+
22+
$composer = json_decode($filesystem->read($composerPath), true);
23+
$section = $this->dev ? 'require-dev' : 'require';
24+
25+
if (isset($composer[$section][$this->packageName])) {
26+
unset($composer[$section][$this->packageName]);
27+
}
28+
29+
$filesystem->write(
30+
$composerPath,
31+
json_encode($composer, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES).PHP_EOL
32+
);
33+
}
34+
35+
public function validate(FilesystemOperator $filesystem): void
36+
{
37+
if (! $filesystem->fileExists('composer.json')) {
38+
throw new \RuntimeException("Cannot remove dependency: 'composer.json' not found.");
39+
}
40+
}
41+
42+
public function getDescription(): string
43+
{
44+
$type = $this->dev ? 'dev ' : '';
45+
46+
return "Removing {$type}composer dependency '{$this->packageName}'";
47+
}
48+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<?php
2+
3+
namespace Aion\Engine\Operations;
4+
5+
use League\Flysystem\FilesystemOperator;
6+
7+
class RemoveComposerScriptOperation implements OperationContract
8+
{
9+
public function __construct(
10+
private readonly string $event,
11+
private readonly string $command
12+
) {}
13+
14+
public function execute(FilesystemOperator $filesystem): void
15+
{
16+
$composerPath = 'composer.json';
17+
18+
if (! $filesystem->fileExists($composerPath)) {
19+
return;
20+
}
21+
22+
$composer = json_decode($filesystem->read($composerPath), true);
23+
24+
if (! isset($composer['scripts'][$this->event])) {
25+
return;
26+
}
27+
28+
// If it's an array, remove the specific command
29+
if (is_array($composer['scripts'][$this->event])) {
30+
$composer['scripts'][$this->event] = array_values(
31+
array_filter(
32+
$composer['scripts'][$this->event],
33+
fn (string $cmd) => $cmd !== $this->command
34+
),
35+
);
36+
37+
// If the array becomes empty, remove the event key entirely
38+
if (empty($composer['scripts'][$this->event])) {
39+
unset($composer['scripts'][$this->event]);
40+
}
41+
} elseif ($composer['scripts'][$this->event] === $this->command) {
42+
// If it's a single string, remove the event key entirely
43+
unset($composer['scripts'][$this->event]);
44+
}
45+
46+
$filesystem->write(
47+
$composerPath,
48+
json_encode($composer, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES).PHP_EOL
49+
);
50+
}
51+
52+
public function validate(FilesystemOperator $filesystem): void
53+
{
54+
if (! $filesystem->fileExists('composer.json')) {
55+
throw new \RuntimeException("Cannot remove script: 'composer.json' not found.");
56+
}
57+
}
58+
59+
public function getDescription(): string
60+
{
61+
return "Removing composer script '{$this->command}' from '{$this->event}'";
62+
}
63+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
namespace Aion\Features\System;
4+
5+
use Aion\Choices\Enums\ConfigKeyEnum;
6+
use Aion\Engine\AionConfig;
7+
use Aion\Engine\Operations\DeleteFolderOperation;
8+
use Aion\Engine\Operations\RemoveComposerDependencyOperation;
9+
use Aion\Engine\Operations\RemoveComposerScriptOperation;
10+
use Aion\Engine\PathResolver;
11+
use Aion\Features\AionFeatureContract;
12+
use Aion\Features\OptionDefinition;
13+
use Aion\Stacks\StackStrategyContract;
14+
15+
readonly class AgenticAiSystemFeature implements AionFeatureContract
16+
{
17+
public static function getOptionSchema(): array
18+
{
19+
return [
20+
ConfigKeyEnum::AgenticAi->value => new OptionDefinition(
21+
label: 'Would you like to add Agentic AI support?',
22+
type: 'confirm',
23+
default: true,
24+
hint: 'Adds laravel boost package and extra AI Guidelines tailored to the shipped architecture'
25+
),
26+
];
27+
}
28+
29+
public function getOperations(StackStrategyContract $stack, AionConfig $config, PathResolver $pathResolver): iterable
30+
{
31+
if ($config->bool(ConfigKeyEnum::AgenticAi, true)) {
32+
return;
33+
}
34+
35+
yield new DeleteFolderOperation('.ai');
36+
yield new RemoveComposerDependencyOperation('laravel/boost', dev: true);
37+
yield new RemoveComposerScriptOperation('post-update-cmd', '@php artisan boost:update --ansi');
38+
}
39+
}

.github/workflows/aion-tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ jobs:
3232
fail-fast: false
3333
matrix:
3434
os: [ubuntu-latest, windows-latest]
35-
profile: [api-stateless, web-stateful, api-stateless-full, api-stateful-lite, oauth-full]
35+
profile: [api-stateless, web-stateful, api-stateless-full, api-stateful-lite, oauth-full-no-ai]
3636

3737
name: ${{ matrix.profile }} on ${{ matrix.os }}
3838

0 commit comments

Comments
 (0)