From 5ac99b1410d1b2e70e789b59c2d7c8ef4ecb80da Mon Sep 17 00:00:00 2001 From: Jordan Partridge Date: Wed, 17 Dec 2025 16:41:33 -0700 Subject: [PATCH 1/3] Allow configurable guideline paths for AI agents This adds the ability to customize where Boost writes AI guideline files (CLAUDE.md, AGENTS.md, etc.) via config and environment variables. --- README.md | 41 +++++++ config/boost.php | 34 +++++- src/Install/CodeEnvironment/ClaudeCode.php | 2 +- src/Install/CodeEnvironment/Codex.php | 2 +- src/Install/CodeEnvironment/Copilot.php | 2 +- src/Install/CodeEnvironment/Cursor.php | 2 +- src/Install/CodeEnvironment/Gemini.php | 2 +- src/Install/CodeEnvironment/OpenCode.php | 2 +- src/Install/CodeEnvironment/PhpStorm.php | 2 +- .../CodeEnvironment/ClaudeCodeTest.php | 93 +++++++++++++++ .../Install/CodeEnvironment/CodexTest.php | 86 ++++++++++++++ .../Install/CodeEnvironment/CopilotTest.php | 70 +++++++++++ .../Install/CodeEnvironment/CursorTest.php | 91 +++++++++++++++ .../Install/CodeEnvironment/GeminiTest.php | 77 +++++++++++++ .../Install/CodeEnvironment/OpenCodeTest.php | 109 ++++++++++++++++++ .../Install/CodeEnvironment/PhpStormTest.php | 99 ++++++++++++++++ 16 files changed, 706 insertions(+), 8 deletions(-) create mode 100644 tests/Unit/Install/CodeEnvironment/ClaudeCodeTest.php create mode 100644 tests/Unit/Install/CodeEnvironment/CodexTest.php create mode 100644 tests/Unit/Install/CodeEnvironment/CopilotTest.php create mode 100644 tests/Unit/Install/CodeEnvironment/CursorTest.php create mode 100644 tests/Unit/Install/CodeEnvironment/GeminiTest.php create mode 100644 tests/Unit/Install/CodeEnvironment/OpenCodeTest.php create mode 100644 tests/Unit/Install/CodeEnvironment/PhpStormTest.php diff --git a/README.md b/README.md index 03b173bf..3e802c2e 100644 --- a/README.md +++ b/README.md @@ -166,6 +166,47 @@ You can override Boost's built-in AI guidelines by creating your own custom guid For example, to override Boost's "Inertia React v2 Form Guidance" guidelines, create a file at `.ai/guidelines/inertia-react/2/forms.blade.php`. When you run `boost:install`, Boost will include your custom guideline instead of the default one. +### Customizing Guideline File Paths + +By default, Boost writes guideline files to your project root (e.g., `CLAUDE.md`, `AGENTS.md`) or agent-specific directories (e.g., `.cursor/rules/laravel-boost.mdc`). You may customize these paths to suit your project structure—for example, consolidating all AI guidelines in a dedicated directory or organizing them within a monorepo. + +First, publish the configuration file: + +```bash +php artisan vendor:publish --tag=boost-config +``` + +Then modify the `agents` section in `config/boost.php`: + +```php +'agents' => [ + 'claude_code' => [ + 'guidelines_path' => 'docs/ai/CLAUDE.md', + ], + 'cursor' => [ + 'guidelines_path' => 'docs/ai/cursor-rules.mdc', + ], +], +``` + +After updating the configuration, regenerate your guidelines: + +```bash +php artisan boost:update +``` + +You may also configure paths via environment variables: + +| Agent | Environment Variable | Default | +|-------|---------------------|---------| +| Claude Code | `BOOST_CLAUDE_GUIDELINES_PATH` | `CLAUDE.md` | +| Codex | `BOOST_CODEX_GUIDELINES_PATH` | `AGENTS.md` | +| Copilot | `BOOST_COPILOT_GUIDELINES_PATH` | `.github/copilot-instructions.md` | +| Cursor | `BOOST_CURSOR_GUIDELINES_PATH` | `.cursor/rules/laravel-boost.mdc` | +| Gemini | `BOOST_GEMINI_GUIDELINES_PATH` | `GEMINI.md` | +| OpenCode | `BOOST_OPENCODE_GUIDELINES_PATH` | `AGENTS.md` | +| PhpStorm (Junie) | `BOOST_PHPSTORM_GUIDELINES_PATH` | `.junie/guidelines.md` | + ## Third-Party Package AI Guidelines If you maintain a third-party package and would like Boost to include AI guidelines for it, you can do so by adding a `resources/boost/guidelines/core.blade.php` file to your package. When users of your package run `php artisan boost:install`, Boost will automatically load your guidelines. diff --git a/config/boost.php b/config/boost.php index d99e4c65..568060b6 100644 --- a/config/boost.php +++ b/config/boost.php @@ -3,7 +3,6 @@ declare(strict_types=1); return [ - /* |-------------------------------------------------------------------------- | Boost Master Switch @@ -29,4 +28,37 @@ 'browser_logs_watcher' => env('BOOST_BROWSER_LOGS_WATCHER', true), + /* + |-------------------------------------------------------------------------- + | Agent Configuration + |-------------------------------------------------------------------------- + | + | Configure paths and settings for specific AI agents. You can customize + | where guideline files are written for each supported agent. + | + */ + + 'agents' => [ + 'claude_code' => [ + 'guidelines_path' => env('BOOST_CLAUDE_GUIDELINES_PATH', 'CLAUDE.md'), + ], + 'codex' => [ + 'guidelines_path' => env('BOOST_CODEX_GUIDELINES_PATH', 'AGENTS.md'), + ], + 'copilot' => [ + 'guidelines_path' => env('BOOST_COPILOT_GUIDELINES_PATH', '.github/copilot-instructions.md'), + ], + 'cursor' => [ + 'guidelines_path' => env('BOOST_CURSOR_GUIDELINES_PATH', '.cursor/rules/laravel-boost.mdc'), + ], + 'gemini' => [ + 'guidelines_path' => env('BOOST_GEMINI_GUIDELINES_PATH', 'GEMINI.md'), + ], + 'opencode' => [ + 'guidelines_path' => env('BOOST_OPENCODE_GUIDELINES_PATH', 'AGENTS.md'), + ], + 'phpstorm' => [ + 'guidelines_path' => env('BOOST_PHPSTORM_GUIDELINES_PATH', '.junie/guidelines.md'), + ], + ], ]; diff --git a/src/Install/CodeEnvironment/ClaudeCode.php b/src/Install/CodeEnvironment/ClaudeCode.php index ed7d58dd..b6676f32 100644 --- a/src/Install/CodeEnvironment/ClaudeCode.php +++ b/src/Install/CodeEnvironment/ClaudeCode.php @@ -53,6 +53,6 @@ public function mcpConfigPath(): string public function guidelinesPath(): string { - return 'CLAUDE.md'; + return config('boost.agents.claude_code.guidelines_path', 'CLAUDE.md'); } } diff --git a/src/Install/CodeEnvironment/Codex.php b/src/Install/CodeEnvironment/Codex.php index 99d5d6d2..52226431 100644 --- a/src/Install/CodeEnvironment/Codex.php +++ b/src/Install/CodeEnvironment/Codex.php @@ -43,7 +43,7 @@ public function projectDetectionConfig(): array public function guidelinesPath(): string { - return 'AGENTS.md'; + return config('boost.agents.codex.guidelines_path', 'AGENTS.md'); } public function mcpInstallationStrategy(): McpInstallationStrategy diff --git a/src/Install/CodeEnvironment/Copilot.php b/src/Install/CodeEnvironment/Copilot.php index 2219539e..7ade8eb5 100644 --- a/src/Install/CodeEnvironment/Copilot.php +++ b/src/Install/CodeEnvironment/Copilot.php @@ -46,6 +46,6 @@ public function mcpClientName(): ?string public function guidelinesPath(): string { - return '.github/copilot-instructions.md'; + return config('boost.agents.copilot.guidelines_path', '.github/copilot-instructions.md'); } } diff --git a/src/Install/CodeEnvironment/Cursor.php b/src/Install/CodeEnvironment/Cursor.php index 8449a5fc..3e9c61ca 100644 --- a/src/Install/CodeEnvironment/Cursor.php +++ b/src/Install/CodeEnvironment/Cursor.php @@ -56,7 +56,7 @@ public function mcpConfigPath(): string public function guidelinesPath(): string { - return '.cursor/rules/laravel-boost.mdc'; + return config('boost.agents.cursor.guidelines_path', '.cursor/rules/laravel-boost.mdc'); } public function frontmatter(): bool diff --git a/src/Install/CodeEnvironment/Gemini.php b/src/Install/CodeEnvironment/Gemini.php index 13e1598b..7b8287df 100644 --- a/src/Install/CodeEnvironment/Gemini.php +++ b/src/Install/CodeEnvironment/Gemini.php @@ -47,6 +47,6 @@ public function mcpConfigPath(): string public function guidelinesPath(): string { - return 'GEMINI.md'; + return config('boost.agents.gemini.guidelines_path', 'GEMINI.md'); } } diff --git a/src/Install/CodeEnvironment/OpenCode.php b/src/Install/CodeEnvironment/OpenCode.php index 1609614b..0d0a0e3f 100644 --- a/src/Install/CodeEnvironment/OpenCode.php +++ b/src/Install/CodeEnvironment/OpenCode.php @@ -52,7 +52,7 @@ public function mcpConfigPath(): string public function guidelinesPath(): string { - return 'AGENTS.md'; + return config('boost.agents.opencode.guidelines_path', 'AGENTS.md'); } public function mcpConfigKey(): string diff --git a/src/Install/CodeEnvironment/PhpStorm.php b/src/Install/CodeEnvironment/PhpStorm.php index 3e495fa0..a73545e8 100644 --- a/src/Install/CodeEnvironment/PhpStorm.php +++ b/src/Install/CodeEnvironment/PhpStorm.php @@ -65,6 +65,6 @@ public function mcpConfigPath(): string public function guidelinesPath(): string { - return '.junie/guidelines.md'; + return config('boost.agents.phpstorm.guidelines_path', '.junie/guidelines.md'); } } diff --git a/tests/Unit/Install/CodeEnvironment/ClaudeCodeTest.php b/tests/Unit/Install/CodeEnvironment/ClaudeCodeTest.php new file mode 100644 index 00000000..a7a1c1c5 --- /dev/null +++ b/tests/Unit/Install/CodeEnvironment/ClaudeCodeTest.php @@ -0,0 +1,93 @@ +strategyFactory = Mockery::mock(DetectionStrategyFactory::class); +}); + +test('name returns claude_code', function (): void { + $claude = new ClaudeCode($this->strategyFactory); + + expect($claude->name())->toBe('claude_code'); +}); + +test('displayName returns Claude Code', function (): void { + $claude = new ClaudeCode($this->strategyFactory); + + expect($claude->displayName())->toBe('Claude Code'); +}); + +test('systemDetectionConfig returns command detection for Darwin', function (): void { + $claude = new ClaudeCode($this->strategyFactory); + + expect($claude->systemDetectionConfig(Platform::Darwin)) + ->toBe(['command' => 'command -v claude']); +}); + +test('systemDetectionConfig returns command detection for Linux', function (): void { + $claude = new ClaudeCode($this->strategyFactory); + + expect($claude->systemDetectionConfig(Platform::Linux)) + ->toBe(['command' => 'command -v claude']); +}); + +test('systemDetectionConfig returns command detection for Windows', function (): void { + $claude = new ClaudeCode($this->strategyFactory); + + expect($claude->systemDetectionConfig(Platform::Windows)) + ->toBe(['command' => 'where claude 2>nul']); +}); + +test('projectDetectionConfig returns paths and files', function (): void { + $claude = new ClaudeCode($this->strategyFactory); + + expect($claude->projectDetectionConfig()) + ->toBe([ + 'paths' => ['.claude'], + 'files' => ['CLAUDE.md'], + ]); +}); + +test('mcpInstallationStrategy returns FILE', function (): void { + $claude = new ClaudeCode($this->strategyFactory); + + expect($claude->mcpInstallationStrategy()) + ->toBe(McpInstallationStrategy::FILE); +}); + +test('mcpConfigPath returns .mcp.json', function (): void { + $claude = new ClaudeCode($this->strategyFactory); + + expect($claude->mcpConfigPath())->toBe('.mcp.json'); +}); + +test('guidelinesPath returns default CLAUDE.md when no config set', function (): void { + $claude = new ClaudeCode($this->strategyFactory); + + expect($claude->guidelinesPath())->toBe('CLAUDE.md'); +}); + +test('guidelinesPath returns custom path from config', function (): void { + config(['boost.agents.claude_code.guidelines_path' => '.claude/CLAUDE.md']); + + $claude = new ClaudeCode($this->strategyFactory); + + expect($claude->guidelinesPath())->toBe('.claude/CLAUDE.md'); +}); + +test('guidelinesPath returns nested custom path from config', function (): void { + config(['boost.agents.claude_code.guidelines_path' => 'docs/ai/CLAUDE.md']); + + $claude = new ClaudeCode($this->strategyFactory); + + expect($claude->guidelinesPath())->toBe('docs/ai/CLAUDE.md'); +}); diff --git a/tests/Unit/Install/CodeEnvironment/CodexTest.php b/tests/Unit/Install/CodeEnvironment/CodexTest.php new file mode 100644 index 00000000..340955ae --- /dev/null +++ b/tests/Unit/Install/CodeEnvironment/CodexTest.php @@ -0,0 +1,86 @@ +strategyFactory = Mockery::mock(DetectionStrategyFactory::class); +}); + +test('name returns codex', function (): void { + $codex = new Codex($this->strategyFactory); + + expect($codex->name())->toBe('codex'); +}); + +test('displayName returns Codex', function (): void { + $codex = new Codex($this->strategyFactory); + + expect($codex->displayName())->toBe('Codex'); +}); + +test('systemDetectionConfig returns command detection for Darwin', function (): void { + $codex = new Codex($this->strategyFactory); + + expect($codex->systemDetectionConfig(Platform::Darwin)) + ->toBe(['command' => 'which codex']); +}); + +test('systemDetectionConfig returns command detection for Linux', function (): void { + $codex = new Codex($this->strategyFactory); + + expect($codex->systemDetectionConfig(Platform::Linux)) + ->toBe(['command' => 'which codex']); +}); + +test('systemDetectionConfig returns command detection for Windows', function (): void { + $codex = new Codex($this->strategyFactory); + + expect($codex->systemDetectionConfig(Platform::Windows)) + ->toBe(['command' => 'where codex 2>nul']); +}); + +test('projectDetectionConfig returns paths and files', function (): void { + $codex = new Codex($this->strategyFactory); + + expect($codex->projectDetectionConfig()) + ->toBe([ + 'paths' => ['.codex'], + 'files' => ['AGENTS.md'], + ]); +}); + +test('mcpInstallationStrategy returns SHELL', function (): void { + $codex = new Codex($this->strategyFactory); + + expect($codex->mcpInstallationStrategy()) + ->toBe(McpInstallationStrategy::SHELL); +}); + +test('shellMcpCommand returns correct command template', function (): void { + $codex = new Codex($this->strategyFactory); + + expect($codex->shellMcpCommand()) + ->toBe('codex mcp add {key} -- {command} {args}'); +}); + +test('guidelinesPath returns default AGENTS.md when no config set', function (): void { + $codex = new Codex($this->strategyFactory); + + expect($codex->guidelinesPath())->toBe('AGENTS.md'); +}); + +test('guidelinesPath returns custom path from config', function (): void { + config(['boost.agents.codex.guidelines_path' => 'docs/AGENTS.md']); + + $codex = new Codex($this->strategyFactory); + + expect($codex->guidelinesPath())->toBe('docs/AGENTS.md'); +}); diff --git a/tests/Unit/Install/CodeEnvironment/CopilotTest.php b/tests/Unit/Install/CodeEnvironment/CopilotTest.php new file mode 100644 index 00000000..d6a5b906 --- /dev/null +++ b/tests/Unit/Install/CodeEnvironment/CopilotTest.php @@ -0,0 +1,70 @@ +strategyFactory = Mockery::mock(DetectionStrategyFactory::class); +}); + +test('name returns copilot', function (): void { + $copilot = new Copilot($this->strategyFactory); + + expect($copilot->name())->toBe('copilot'); +}); + +test('displayName returns GitHub Copilot', function (): void { + $copilot = new Copilot($this->strategyFactory); + + expect($copilot->displayName())->toBe('GitHub Copilot'); +}); + +test('systemDetectionConfig returns empty files array', function (): void { + $copilot = new Copilot($this->strategyFactory); + + expect($copilot->systemDetectionConfig(Platform::Darwin)) + ->toBe(['files' => []]); +}); + +test('projectDetectionConfig returns copilot instructions file', function (): void { + $copilot = new Copilot($this->strategyFactory); + + expect($copilot->projectDetectionConfig()) + ->toBe([ + 'files' => ['.github/copilot-instructions.md'], + ]); +}); + +test('detectOnSystem always returns false', function (): void { + $copilot = new Copilot($this->strategyFactory); + + expect($copilot->detectOnSystem(Platform::Darwin))->toBeFalse() + ->and($copilot->detectOnSystem(Platform::Linux))->toBeFalse() + ->and($copilot->detectOnSystem(Platform::Windows))->toBeFalse(); +}); + +test('mcpClientName returns null', function (): void { + $copilot = new Copilot($this->strategyFactory); + + expect($copilot->mcpClientName())->toBeNull(); +}); + +test('guidelinesPath returns default path when no config set', function (): void { + $copilot = new Copilot($this->strategyFactory); + + expect($copilot->guidelinesPath())->toBe('.github/copilot-instructions.md'); +}); + +test('guidelinesPath returns custom path from config', function (): void { + config(['boost.agents.copilot.guidelines_path' => 'docs/copilot.md']); + + $copilot = new Copilot($this->strategyFactory); + + expect($copilot->guidelinesPath())->toBe('docs/copilot.md'); +}); diff --git a/tests/Unit/Install/CodeEnvironment/CursorTest.php b/tests/Unit/Install/CodeEnvironment/CursorTest.php new file mode 100644 index 00000000..db579209 --- /dev/null +++ b/tests/Unit/Install/CodeEnvironment/CursorTest.php @@ -0,0 +1,91 @@ +strategyFactory = Mockery::mock(DetectionStrategyFactory::class); +}); + +test('name returns cursor', function (): void { + $cursor = new Cursor($this->strategyFactory); + + expect($cursor->name())->toBe('cursor'); +}); + +test('displayName returns Cursor', function (): void { + $cursor = new Cursor($this->strategyFactory); + + expect($cursor->displayName())->toBe('Cursor'); +}); + +test('systemDetectionConfig returns paths for Darwin', function (): void { + $cursor = new Cursor($this->strategyFactory); + + expect($cursor->systemDetectionConfig(Platform::Darwin)) + ->toBe(['paths' => ['/Applications/Cursor.app']]); +}); + +test('systemDetectionConfig returns paths for Linux', function (): void { + $cursor = new Cursor($this->strategyFactory); + + expect($cursor->systemDetectionConfig(Platform::Linux)) + ->toBe([ + 'paths' => [ + '/opt/cursor', + '/usr/local/bin/cursor', + '~/.local/bin/cursor', + ], + ]); +}); + +test('systemDetectionConfig returns paths for Windows', function (): void { + $cursor = new Cursor($this->strategyFactory); + + expect($cursor->systemDetectionConfig(Platform::Windows)) + ->toBe([ + 'paths' => [ + '%ProgramFiles%\\Cursor', + '%LOCALAPPDATA%\\Programs\\Cursor', + ], + ]); +}); + +test('projectDetectionConfig returns cursor directory', function (): void { + $cursor = new Cursor($this->strategyFactory); + + expect($cursor->projectDetectionConfig()) + ->toBe(['paths' => ['.cursor']]); +}); + +test('mcpConfigPath returns cursor mcp.json path', function (): void { + $cursor = new Cursor($this->strategyFactory); + + expect($cursor->mcpConfigPath())->toBe('.cursor/mcp.json'); +}); + +test('frontmatter returns true', function (): void { + $cursor = new Cursor($this->strategyFactory); + + expect($cursor->frontmatter())->toBeTrue(); +}); + +test('guidelinesPath returns default path when no config set', function (): void { + $cursor = new Cursor($this->strategyFactory); + + expect($cursor->guidelinesPath())->toBe('.cursor/rules/laravel-boost.mdc'); +}); + +test('guidelinesPath returns custom path from config', function (): void { + config(['boost.agents.cursor.guidelines_path' => '.cursor/custom-rules.mdc']); + + $cursor = new Cursor($this->strategyFactory); + + expect($cursor->guidelinesPath())->toBe('.cursor/custom-rules.mdc'); +}); diff --git a/tests/Unit/Install/CodeEnvironment/GeminiTest.php b/tests/Unit/Install/CodeEnvironment/GeminiTest.php new file mode 100644 index 00000000..3d1a787e --- /dev/null +++ b/tests/Unit/Install/CodeEnvironment/GeminiTest.php @@ -0,0 +1,77 @@ +strategyFactory = Mockery::mock(DetectionStrategyFactory::class); +}); + +test('name returns gemini', function (): void { + $gemini = new Gemini($this->strategyFactory); + + expect($gemini->name())->toBe('gemini'); +}); + +test('displayName returns Gemini', function (): void { + $gemini = new Gemini($this->strategyFactory); + + expect($gemini->displayName())->toBe('Gemini'); +}); + +test('systemDetectionConfig returns command detection for Darwin', function (): void { + $gemini = new Gemini($this->strategyFactory); + + expect($gemini->systemDetectionConfig(Platform::Darwin)) + ->toBe(['command' => 'command -v gemini']); +}); + +test('systemDetectionConfig returns command detection for Linux', function (): void { + $gemini = new Gemini($this->strategyFactory); + + expect($gemini->systemDetectionConfig(Platform::Linux)) + ->toBe(['command' => 'command -v gemini']); +}); + +test('systemDetectionConfig returns command detection for Windows', function (): void { + $gemini = new Gemini($this->strategyFactory); + + expect($gemini->systemDetectionConfig(Platform::Windows)) + ->toBe(['command' => 'where gemini 2>nul']); +}); + +test('projectDetectionConfig returns paths and files', function (): void { + $gemini = new Gemini($this->strategyFactory); + + expect($gemini->projectDetectionConfig()) + ->toBe([ + 'paths' => ['.gemini'], + 'files' => ['GEMINI.md'], + ]); +}); + +test('mcpConfigPath returns gemini settings path', function (): void { + $gemini = new Gemini($this->strategyFactory); + + expect($gemini->mcpConfigPath())->toBe('.gemini/settings.json'); +}); + +test('guidelinesPath returns default GEMINI.md when no config set', function (): void { + $gemini = new Gemini($this->strategyFactory); + + expect($gemini->guidelinesPath())->toBe('GEMINI.md'); +}); + +test('guidelinesPath returns custom path from config', function (): void { + config(['boost.agents.gemini.guidelines_path' => 'docs/GEMINI.md']); + + $gemini = new Gemini($this->strategyFactory); + + expect($gemini->guidelinesPath())->toBe('docs/GEMINI.md'); +}); diff --git a/tests/Unit/Install/CodeEnvironment/OpenCodeTest.php b/tests/Unit/Install/CodeEnvironment/OpenCodeTest.php new file mode 100644 index 00000000..eac2d3f6 --- /dev/null +++ b/tests/Unit/Install/CodeEnvironment/OpenCodeTest.php @@ -0,0 +1,109 @@ +strategyFactory = Mockery::mock(DetectionStrategyFactory::class); +}); + +test('name returns opencode', function (): void { + $opencode = new OpenCode($this->strategyFactory); + + expect($opencode->name())->toBe('opencode'); +}); + +test('displayName returns OpenCode', function (): void { + $opencode = new OpenCode($this->strategyFactory); + + expect($opencode->displayName())->toBe('OpenCode'); +}); + +test('systemDetectionConfig returns command detection for Darwin', function (): void { + $opencode = new OpenCode($this->strategyFactory); + + expect($opencode->systemDetectionConfig(Platform::Darwin)) + ->toBe(['command' => 'command -v opencode']); +}); + +test('systemDetectionConfig returns command detection for Linux', function (): void { + $opencode = new OpenCode($this->strategyFactory); + + expect($opencode->systemDetectionConfig(Platform::Linux)) + ->toBe(['command' => 'command -v opencode']); +}); + +test('systemDetectionConfig returns command detection for Windows', function (): void { + $opencode = new OpenCode($this->strategyFactory); + + expect($opencode->systemDetectionConfig(Platform::Windows)) + ->toBe(['command' => 'where opencode 2>nul']); +}); + +test('projectDetectionConfig returns files', function (): void { + $opencode = new OpenCode($this->strategyFactory); + + expect($opencode->projectDetectionConfig()) + ->toBe([ + 'files' => ['AGENTS.md', 'opencode.json'], + ]); +}); + +test('mcpInstallationStrategy returns FILE', function (): void { + $opencode = new OpenCode($this->strategyFactory); + + expect($opencode->mcpInstallationStrategy()) + ->toBe(McpInstallationStrategy::FILE); +}); + +test('mcpConfigPath returns opencode.json', function (): void { + $opencode = new OpenCode($this->strategyFactory); + + expect($opencode->mcpConfigPath())->toBe('opencode.json'); +}); + +test('mcpConfigKey returns mcp', function (): void { + $opencode = new OpenCode($this->strategyFactory); + + expect($opencode->mcpConfigKey())->toBe('mcp'); +}); + +test('defaultMcpConfig returns schema', function (): void { + $opencode = new OpenCode($this->strategyFactory); + + expect($opencode->defaultMcpConfig()) + ->toBe(['$schema' => 'https://opencode.ai/config.json']); +}); + +test('mcpServerConfig returns correct structure', function (): void { + $opencode = new OpenCode($this->strategyFactory); + + expect($opencode->mcpServerConfig('php', ['artisan', 'boost:mcp'], ['APP_ENV' => 'local'])) + ->toBe([ + 'type' => 'local', + 'enabled' => true, + 'command' => ['php', 'artisan', 'boost:mcp'], + 'environment' => ['APP_ENV' => 'local'], + ]); +}); + +test('guidelinesPath returns default AGENTS.md when no config set', function (): void { + $opencode = new OpenCode($this->strategyFactory); + + expect($opencode->guidelinesPath())->toBe('AGENTS.md'); +}); + +test('guidelinesPath returns custom path from config', function (): void { + config(['boost.agents.opencode.guidelines_path' => 'docs/AGENTS.md']); + + $opencode = new OpenCode($this->strategyFactory); + + expect($opencode->guidelinesPath())->toBe('docs/AGENTS.md'); +}); diff --git a/tests/Unit/Install/CodeEnvironment/PhpStormTest.php b/tests/Unit/Install/CodeEnvironment/PhpStormTest.php new file mode 100644 index 00000000..91e76ca6 --- /dev/null +++ b/tests/Unit/Install/CodeEnvironment/PhpStormTest.php @@ -0,0 +1,99 @@ +strategyFactory = Mockery::mock(DetectionStrategyFactory::class); +}); + +test('name returns phpstorm', function (): void { + $phpstorm = new PhpStorm($this->strategyFactory); + + expect($phpstorm->name())->toBe('phpstorm'); +}); + +test('displayName returns PhpStorm', function (): void { + $phpstorm = new PhpStorm($this->strategyFactory); + + expect($phpstorm->displayName())->toBe('PhpStorm'); +}); + +test('useAbsolutePathForMcp is true', function (): void { + $phpstorm = new PhpStorm($this->strategyFactory); + + expect($phpstorm->useAbsolutePathForMcp)->toBeTrue(); +}); + +test('systemDetectionConfig returns paths for Darwin', function (): void { + $phpstorm = new PhpStorm($this->strategyFactory); + + expect($phpstorm->systemDetectionConfig(Platform::Darwin)) + ->toBe(['paths' => ['/Applications/PhpStorm.app']]); +}); + +test('systemDetectionConfig returns paths for Linux', function (): void { + $phpstorm = new PhpStorm($this->strategyFactory); + + expect($phpstorm->systemDetectionConfig(Platform::Linux)) + ->toBe([ + 'paths' => [ + '/opt/phpstorm', + '/opt/PhpStorm*', + '/usr/local/bin/phpstorm', + '~/.local/share/JetBrains/Toolbox/apps/PhpStorm/ch-*', + ], + ]); +}); + +test('systemDetectionConfig returns paths for Windows', function (): void { + $phpstorm = new PhpStorm($this->strategyFactory); + + expect($phpstorm->systemDetectionConfig(Platform::Windows)) + ->toBe([ + 'paths' => [ + '%ProgramFiles%\\JetBrains\\PhpStorm*', + '%LOCALAPPDATA%\\JetBrains\\Toolbox\\apps\\PhpStorm\\ch-*', + '%LOCALAPPDATA%\\Programs\\PhpStorm', + ], + ]); +}); + +test('projectDetectionConfig returns idea and junie paths', function (): void { + $phpstorm = new PhpStorm($this->strategyFactory); + + expect($phpstorm->projectDetectionConfig()) + ->toBe(['paths' => ['.idea', '.junie']]); +}); + +test('agentName returns Junie', function (): void { + $phpstorm = new PhpStorm($this->strategyFactory); + + expect($phpstorm->agentName())->toBe('Junie'); +}); + +test('mcpConfigPath returns junie mcp path', function (): void { + $phpstorm = new PhpStorm($this->strategyFactory); + + expect($phpstorm->mcpConfigPath())->toBe('.junie/mcp/mcp.json'); +}); + +test('guidelinesPath returns default path when no config set', function (): void { + $phpstorm = new PhpStorm($this->strategyFactory); + + expect($phpstorm->guidelinesPath())->toBe('.junie/guidelines.md'); +}); + +test('guidelinesPath returns custom path from config', function (): void { + config(['boost.agents.phpstorm.guidelines_path' => '.idea/guidelines.md']); + + $phpstorm = new PhpStorm($this->strategyFactory); + + expect($phpstorm->guidelinesPath())->toBe('.idea/guidelines.md'); +}); From db9f6847232df0c7c057b57d2994ec2d26abf7cf Mon Sep 17 00:00:00 2001 From: Pushpak Chhajed Date: Thu, 18 Dec 2025 20:32:31 +0530 Subject: [PATCH 2/3] Refactor guideline paths configuration Signed-off-by: Pushpak Chhajed --- README.md | 41 ---------- config/boost.php | 34 -------- src/Install/CodeEnvironment/ClaudeCode.php | 2 +- src/Install/CodeEnvironment/Codex.php | 2 +- src/Install/CodeEnvironment/Copilot.php | 2 +- src/Install/CodeEnvironment/Cursor.php | 2 +- src/Install/CodeEnvironment/Gemini.php | 2 +- src/Install/CodeEnvironment/OpenCode.php | 2 +- src/Install/CodeEnvironment/PhpStorm.php | 2 +- .../CodeEnvironment/ClaudeCodeTest.php | 66 +++------------- .../Install/CodeEnvironment/CodexTest.php | 63 ++------------- .../Install/CodeEnvironment/CopilotTest.php | 40 +--------- .../Install/CodeEnvironment/CursorTest.php | 70 +++-------------- .../Install/CodeEnvironment/GeminiTest.php | 54 ++----------- .../Install/CodeEnvironment/OpenCodeTest.php | 74 ++---------------- .../Install/CodeEnvironment/PhpStormTest.php | 78 +++---------------- 16 files changed, 62 insertions(+), 472 deletions(-) diff --git a/README.md b/README.md index 3e802c2e..03b173bf 100644 --- a/README.md +++ b/README.md @@ -166,47 +166,6 @@ You can override Boost's built-in AI guidelines by creating your own custom guid For example, to override Boost's "Inertia React v2 Form Guidance" guidelines, create a file at `.ai/guidelines/inertia-react/2/forms.blade.php`. When you run `boost:install`, Boost will include your custom guideline instead of the default one. -### Customizing Guideline File Paths - -By default, Boost writes guideline files to your project root (e.g., `CLAUDE.md`, `AGENTS.md`) or agent-specific directories (e.g., `.cursor/rules/laravel-boost.mdc`). You may customize these paths to suit your project structure—for example, consolidating all AI guidelines in a dedicated directory or organizing them within a monorepo. - -First, publish the configuration file: - -```bash -php artisan vendor:publish --tag=boost-config -``` - -Then modify the `agents` section in `config/boost.php`: - -```php -'agents' => [ - 'claude_code' => [ - 'guidelines_path' => 'docs/ai/CLAUDE.md', - ], - 'cursor' => [ - 'guidelines_path' => 'docs/ai/cursor-rules.mdc', - ], -], -``` - -After updating the configuration, regenerate your guidelines: - -```bash -php artisan boost:update -``` - -You may also configure paths via environment variables: - -| Agent | Environment Variable | Default | -|-------|---------------------|---------| -| Claude Code | `BOOST_CLAUDE_GUIDELINES_PATH` | `CLAUDE.md` | -| Codex | `BOOST_CODEX_GUIDELINES_PATH` | `AGENTS.md` | -| Copilot | `BOOST_COPILOT_GUIDELINES_PATH` | `.github/copilot-instructions.md` | -| Cursor | `BOOST_CURSOR_GUIDELINES_PATH` | `.cursor/rules/laravel-boost.mdc` | -| Gemini | `BOOST_GEMINI_GUIDELINES_PATH` | `GEMINI.md` | -| OpenCode | `BOOST_OPENCODE_GUIDELINES_PATH` | `AGENTS.md` | -| PhpStorm (Junie) | `BOOST_PHPSTORM_GUIDELINES_PATH` | `.junie/guidelines.md` | - ## Third-Party Package AI Guidelines If you maintain a third-party package and would like Boost to include AI guidelines for it, you can do so by adding a `resources/boost/guidelines/core.blade.php` file to your package. When users of your package run `php artisan boost:install`, Boost will automatically load your guidelines. diff --git a/config/boost.php b/config/boost.php index 568060b6..68b312d6 100644 --- a/config/boost.php +++ b/config/boost.php @@ -27,38 +27,4 @@ */ 'browser_logs_watcher' => env('BOOST_BROWSER_LOGS_WATCHER', true), - - /* - |-------------------------------------------------------------------------- - | Agent Configuration - |-------------------------------------------------------------------------- - | - | Configure paths and settings for specific AI agents. You can customize - | where guideline files are written for each supported agent. - | - */ - - 'agents' => [ - 'claude_code' => [ - 'guidelines_path' => env('BOOST_CLAUDE_GUIDELINES_PATH', 'CLAUDE.md'), - ], - 'codex' => [ - 'guidelines_path' => env('BOOST_CODEX_GUIDELINES_PATH', 'AGENTS.md'), - ], - 'copilot' => [ - 'guidelines_path' => env('BOOST_COPILOT_GUIDELINES_PATH', '.github/copilot-instructions.md'), - ], - 'cursor' => [ - 'guidelines_path' => env('BOOST_CURSOR_GUIDELINES_PATH', '.cursor/rules/laravel-boost.mdc'), - ], - 'gemini' => [ - 'guidelines_path' => env('BOOST_GEMINI_GUIDELINES_PATH', 'GEMINI.md'), - ], - 'opencode' => [ - 'guidelines_path' => env('BOOST_OPENCODE_GUIDELINES_PATH', 'AGENTS.md'), - ], - 'phpstorm' => [ - 'guidelines_path' => env('BOOST_PHPSTORM_GUIDELINES_PATH', '.junie/guidelines.md'), - ], - ], ]; diff --git a/src/Install/CodeEnvironment/ClaudeCode.php b/src/Install/CodeEnvironment/ClaudeCode.php index b6676f32..0e4cdd4e 100644 --- a/src/Install/CodeEnvironment/ClaudeCode.php +++ b/src/Install/CodeEnvironment/ClaudeCode.php @@ -53,6 +53,6 @@ public function mcpConfigPath(): string public function guidelinesPath(): string { - return config('boost.agents.claude_code.guidelines_path', 'CLAUDE.md'); + return config('boost.code_environments.claude_code.guidelines_path', 'CLAUDE.md'); } } diff --git a/src/Install/CodeEnvironment/Codex.php b/src/Install/CodeEnvironment/Codex.php index 52226431..9e07278b 100644 --- a/src/Install/CodeEnvironment/Codex.php +++ b/src/Install/CodeEnvironment/Codex.php @@ -43,7 +43,7 @@ public function projectDetectionConfig(): array public function guidelinesPath(): string { - return config('boost.agents.codex.guidelines_path', 'AGENTS.md'); + return config('boost.code_environments.codex.guidelines_path', 'AGENTS.md'); } public function mcpInstallationStrategy(): McpInstallationStrategy diff --git a/src/Install/CodeEnvironment/Copilot.php b/src/Install/CodeEnvironment/Copilot.php index 7ade8eb5..96bea149 100644 --- a/src/Install/CodeEnvironment/Copilot.php +++ b/src/Install/CodeEnvironment/Copilot.php @@ -46,6 +46,6 @@ public function mcpClientName(): ?string public function guidelinesPath(): string { - return config('boost.agents.copilot.guidelines_path', '.github/copilot-instructions.md'); + return config('boost.code_environments.copilot.guidelines_path', '.github/copilot-instructions.md'); } } diff --git a/src/Install/CodeEnvironment/Cursor.php b/src/Install/CodeEnvironment/Cursor.php index 3e9c61ca..af87c901 100644 --- a/src/Install/CodeEnvironment/Cursor.php +++ b/src/Install/CodeEnvironment/Cursor.php @@ -56,7 +56,7 @@ public function mcpConfigPath(): string public function guidelinesPath(): string { - return config('boost.agents.cursor.guidelines_path', '.cursor/rules/laravel-boost.mdc'); + return config('boost.code_environments.cursor.guidelines_path', '.cursor/rules/laravel-boost.mdc'); } public function frontmatter(): bool diff --git a/src/Install/CodeEnvironment/Gemini.php b/src/Install/CodeEnvironment/Gemini.php index 7b8287df..d128db4b 100644 --- a/src/Install/CodeEnvironment/Gemini.php +++ b/src/Install/CodeEnvironment/Gemini.php @@ -47,6 +47,6 @@ public function mcpConfigPath(): string public function guidelinesPath(): string { - return config('boost.agents.gemini.guidelines_path', 'GEMINI.md'); + return config('boost.code_environments.gemini.guidelines_path', 'GEMINI.md'); } } diff --git a/src/Install/CodeEnvironment/OpenCode.php b/src/Install/CodeEnvironment/OpenCode.php index 0d0a0e3f..cf54f1d5 100644 --- a/src/Install/CodeEnvironment/OpenCode.php +++ b/src/Install/CodeEnvironment/OpenCode.php @@ -52,7 +52,7 @@ public function mcpConfigPath(): string public function guidelinesPath(): string { - return config('boost.agents.opencode.guidelines_path', 'AGENTS.md'); + return config('boost.code_environments.opencode.guidelines_path', 'AGENTS.md'); } public function mcpConfigKey(): string diff --git a/src/Install/CodeEnvironment/PhpStorm.php b/src/Install/CodeEnvironment/PhpStorm.php index a73545e8..39d5f882 100644 --- a/src/Install/CodeEnvironment/PhpStorm.php +++ b/src/Install/CodeEnvironment/PhpStorm.php @@ -65,6 +65,6 @@ public function mcpConfigPath(): string public function guidelinesPath(): string { - return config('boost.agents.phpstorm.guidelines_path', '.junie/guidelines.md'); + return config('boost.code_environments.phpstorm.guidelines_path', '.junie/guidelines.md'); } } diff --git a/tests/Unit/Install/CodeEnvironment/ClaudeCodeTest.php b/tests/Unit/Install/CodeEnvironment/ClaudeCodeTest.php index a7a1c1c5..5de5fdfd 100644 --- a/tests/Unit/Install/CodeEnvironment/ClaudeCodeTest.php +++ b/tests/Unit/Install/CodeEnvironment/ClaudeCodeTest.php @@ -6,7 +6,6 @@ use Laravel\Boost\Install\CodeEnvironment\ClaudeCode; use Laravel\Boost\Install\Detection\DetectionStrategyFactory; -use Laravel\Boost\Install\Enums\McpInstallationStrategy; use Laravel\Boost\Install\Enums\Platform; use Mockery; @@ -14,60 +13,15 @@ $this->strategyFactory = Mockery::mock(DetectionStrategyFactory::class); }); -test('name returns claude_code', function (): void { - $claude = new ClaudeCode($this->strategyFactory); - - expect($claude->name())->toBe('claude_code'); -}); - -test('displayName returns Claude Code', function (): void { - $claude = new ClaudeCode($this->strategyFactory); - - expect($claude->displayName())->toBe('Claude Code'); -}); - -test('systemDetectionConfig returns command detection for Darwin', function (): void { +test('systemDetectionConfig returns command-based detection for all platforms', function (): void { $claude = new ClaudeCode($this->strategyFactory); expect($claude->systemDetectionConfig(Platform::Darwin)) - ->toBe(['command' => 'command -v claude']); -}); - -test('systemDetectionConfig returns command detection for Linux', function (): void { - $claude = new ClaudeCode($this->strategyFactory); - - expect($claude->systemDetectionConfig(Platform::Linux)) - ->toBe(['command' => 'command -v claude']); -}); - -test('systemDetectionConfig returns command detection for Windows', function (): void { - $claude = new ClaudeCode($this->strategyFactory); - - expect($claude->systemDetectionConfig(Platform::Windows)) - ->toBe(['command' => 'where claude 2>nul']); -}); - -test('projectDetectionConfig returns paths and files', function (): void { - $claude = new ClaudeCode($this->strategyFactory); - - expect($claude->projectDetectionConfig()) - ->toBe([ - 'paths' => ['.claude'], - 'files' => ['CLAUDE.md'], - ]); -}); - -test('mcpInstallationStrategy returns FILE', function (): void { - $claude = new ClaudeCode($this->strategyFactory); - - expect($claude->mcpInstallationStrategy()) - ->toBe(McpInstallationStrategy::FILE); -}); - -test('mcpConfigPath returns .mcp.json', function (): void { - $claude = new ClaudeCode($this->strategyFactory); - - expect($claude->mcpConfigPath())->toBe('.mcp.json'); + ->toHaveKey('command') + ->and($claude->systemDetectionConfig(Platform::Linux)) + ->toHaveKey('command') + ->and($claude->systemDetectionConfig(Platform::Windows)) + ->toHaveKey('command'); }); test('guidelinesPath returns default CLAUDE.md when no config set', function (): void { @@ -76,16 +30,16 @@ expect($claude->guidelinesPath())->toBe('CLAUDE.md'); }); -test('guidelinesPath returns custom path from config', function (): void { - config(['boost.agents.claude_code.guidelines_path' => '.claude/CLAUDE.md']); +test('guidelinesPath returns a custom path from config', function (): void { + config(['boost.code_environments.claude_code.guidelines_path' => '.claude/CLAUDE.md']); $claude = new ClaudeCode($this->strategyFactory); expect($claude->guidelinesPath())->toBe('.claude/CLAUDE.md'); }); -test('guidelinesPath returns nested custom path from config', function (): void { - config(['boost.agents.claude_code.guidelines_path' => 'docs/ai/CLAUDE.md']); +test('guidelinesPath returns a nested custom path from config', function (): void { + config(['boost.code_environments.claude_code.guidelines_path' => 'docs/ai/CLAUDE.md']); $claude = new ClaudeCode($this->strategyFactory); diff --git a/tests/Unit/Install/CodeEnvironment/CodexTest.php b/tests/Unit/Install/CodeEnvironment/CodexTest.php index 340955ae..9bd18249 100644 --- a/tests/Unit/Install/CodeEnvironment/CodexTest.php +++ b/tests/Unit/Install/CodeEnvironment/CodexTest.php @@ -6,7 +6,6 @@ use Laravel\Boost\Install\CodeEnvironment\Codex; use Laravel\Boost\Install\Detection\DetectionStrategyFactory; -use Laravel\Boost\Install\Enums\McpInstallationStrategy; use Laravel\Boost\Install\Enums\Platform; use Mockery; @@ -14,61 +13,15 @@ $this->strategyFactory = Mockery::mock(DetectionStrategyFactory::class); }); -test('name returns codex', function (): void { - $codex = new Codex($this->strategyFactory); - - expect($codex->name())->toBe('codex'); -}); - -test('displayName returns Codex', function (): void { - $codex = new Codex($this->strategyFactory); - - expect($codex->displayName())->toBe('Codex'); -}); - -test('systemDetectionConfig returns command detection for Darwin', function (): void { +test('systemDetectionConfig returns command-based detection for all platforms', function (): void { $codex = new Codex($this->strategyFactory); expect($codex->systemDetectionConfig(Platform::Darwin)) - ->toBe(['command' => 'which codex']); -}); - -test('systemDetectionConfig returns command detection for Linux', function (): void { - $codex = new Codex($this->strategyFactory); - - expect($codex->systemDetectionConfig(Platform::Linux)) - ->toBe(['command' => 'which codex']); -}); - -test('systemDetectionConfig returns command detection for Windows', function (): void { - $codex = new Codex($this->strategyFactory); - - expect($codex->systemDetectionConfig(Platform::Windows)) - ->toBe(['command' => 'where codex 2>nul']); -}); - -test('projectDetectionConfig returns paths and files', function (): void { - $codex = new Codex($this->strategyFactory); - - expect($codex->projectDetectionConfig()) - ->toBe([ - 'paths' => ['.codex'], - 'files' => ['AGENTS.md'], - ]); -}); - -test('mcpInstallationStrategy returns SHELL', function (): void { - $codex = new Codex($this->strategyFactory); - - expect($codex->mcpInstallationStrategy()) - ->toBe(McpInstallationStrategy::SHELL); -}); - -test('shellMcpCommand returns correct command template', function (): void { - $codex = new Codex($this->strategyFactory); - - expect($codex->shellMcpCommand()) - ->toBe('codex mcp add {key} -- {command} {args}'); + ->toHaveKey('command') + ->and($codex->systemDetectionConfig(Platform::Linux)) + ->toHaveKey('command') + ->and($codex->systemDetectionConfig(Platform::Windows)) + ->toHaveKey('command'); }); test('guidelinesPath returns default AGENTS.md when no config set', function (): void { @@ -77,8 +30,8 @@ expect($codex->guidelinesPath())->toBe('AGENTS.md'); }); -test('guidelinesPath returns custom path from config', function (): void { - config(['boost.agents.codex.guidelines_path' => 'docs/AGENTS.md']); +test('guidelinesPath returns a custom path from config', function (): void { + config(['boost.code_environments.codex.guidelines_path' => 'docs/AGENTS.md']); $codex = new Codex($this->strategyFactory); diff --git a/tests/Unit/Install/CodeEnvironment/CopilotTest.php b/tests/Unit/Install/CodeEnvironment/CopilotTest.php index d6a5b906..640f69b7 100644 --- a/tests/Unit/Install/CodeEnvironment/CopilotTest.php +++ b/tests/Unit/Install/CodeEnvironment/CopilotTest.php @@ -13,34 +13,6 @@ $this->strategyFactory = Mockery::mock(DetectionStrategyFactory::class); }); -test('name returns copilot', function (): void { - $copilot = new Copilot($this->strategyFactory); - - expect($copilot->name())->toBe('copilot'); -}); - -test('displayName returns GitHub Copilot', function (): void { - $copilot = new Copilot($this->strategyFactory); - - expect($copilot->displayName())->toBe('GitHub Copilot'); -}); - -test('systemDetectionConfig returns empty files array', function (): void { - $copilot = new Copilot($this->strategyFactory); - - expect($copilot->systemDetectionConfig(Platform::Darwin)) - ->toBe(['files' => []]); -}); - -test('projectDetectionConfig returns copilot instructions file', function (): void { - $copilot = new Copilot($this->strategyFactory); - - expect($copilot->projectDetectionConfig()) - ->toBe([ - 'files' => ['.github/copilot-instructions.md'], - ]); -}); - test('detectOnSystem always returns false', function (): void { $copilot = new Copilot($this->strategyFactory); @@ -49,20 +21,14 @@ ->and($copilot->detectOnSystem(Platform::Windows))->toBeFalse(); }); -test('mcpClientName returns null', function (): void { - $copilot = new Copilot($this->strategyFactory); - - expect($copilot->mcpClientName())->toBeNull(); -}); - -test('guidelinesPath returns default path when no config set', function (): void { +test('guidelinesPath returns a default path when no config set', function (): void { $copilot = new Copilot($this->strategyFactory); expect($copilot->guidelinesPath())->toBe('.github/copilot-instructions.md'); }); -test('guidelinesPath returns custom path from config', function (): void { - config(['boost.agents.copilot.guidelines_path' => 'docs/copilot.md']); +test('guidelinesPath returns a custom path from config', function (): void { + config(['boost.code_environments.copilot.guidelines_path' => 'docs/copilot.md']); $copilot = new Copilot($this->strategyFactory); diff --git a/tests/Unit/Install/CodeEnvironment/CursorTest.php b/tests/Unit/Install/CodeEnvironment/CursorTest.php index db579209..4c7bf4c7 100644 --- a/tests/Unit/Install/CodeEnvironment/CursorTest.php +++ b/tests/Unit/Install/CodeEnvironment/CursorTest.php @@ -13,77 +13,25 @@ $this->strategyFactory = Mockery::mock(DetectionStrategyFactory::class); }); -test('name returns cursor', function (): void { - $cursor = new Cursor($this->strategyFactory); - - expect($cursor->name())->toBe('cursor'); -}); - -test('displayName returns Cursor', function (): void { - $cursor = new Cursor($this->strategyFactory); - - expect($cursor->displayName())->toBe('Cursor'); -}); - -test('systemDetectionConfig returns paths for Darwin', function (): void { +test('systemDetectionConfig returns path-based detection for all platforms', function (): void { $cursor = new Cursor($this->strategyFactory); expect($cursor->systemDetectionConfig(Platform::Darwin)) - ->toBe(['paths' => ['/Applications/Cursor.app']]); -}); - -test('systemDetectionConfig returns paths for Linux', function (): void { - $cursor = new Cursor($this->strategyFactory); - - expect($cursor->systemDetectionConfig(Platform::Linux)) - ->toBe([ - 'paths' => [ - '/opt/cursor', - '/usr/local/bin/cursor', - '~/.local/bin/cursor', - ], - ]); -}); - -test('systemDetectionConfig returns paths for Windows', function (): void { - $cursor = new Cursor($this->strategyFactory); - - expect($cursor->systemDetectionConfig(Platform::Windows)) - ->toBe([ - 'paths' => [ - '%ProgramFiles%\\Cursor', - '%LOCALAPPDATA%\\Programs\\Cursor', - ], - ]); -}); - -test('projectDetectionConfig returns cursor directory', function (): void { - $cursor = new Cursor($this->strategyFactory); - - expect($cursor->projectDetectionConfig()) - ->toBe(['paths' => ['.cursor']]); -}); - -test('mcpConfigPath returns cursor mcp.json path', function (): void { - $cursor = new Cursor($this->strategyFactory); - - expect($cursor->mcpConfigPath())->toBe('.cursor/mcp.json'); -}); - -test('frontmatter returns true', function (): void { - $cursor = new Cursor($this->strategyFactory); - - expect($cursor->frontmatter())->toBeTrue(); + ->toHaveKey('paths') + ->and($cursor->systemDetectionConfig(Platform::Linux)) + ->toHaveKey('paths') + ->and($cursor->systemDetectionConfig(Platform::Windows)) + ->toHaveKey('paths'); }); -test('guidelinesPath returns default path when no config set', function (): void { +test('guidelinesPath returns a default path when no config is set', function (): void { $cursor = new Cursor($this->strategyFactory); expect($cursor->guidelinesPath())->toBe('.cursor/rules/laravel-boost.mdc'); }); -test('guidelinesPath returns custom path from config', function (): void { - config(['boost.agents.cursor.guidelines_path' => '.cursor/custom-rules.mdc']); +test('guidelinesPath returns a custom path from config', function (): void { + config(['boost.code_environments.cursor.guidelines_path' => '.cursor/custom-rules.mdc']); $cursor = new Cursor($this->strategyFactory); diff --git a/tests/Unit/Install/CodeEnvironment/GeminiTest.php b/tests/Unit/Install/CodeEnvironment/GeminiTest.php index 3d1a787e..d4438534 100644 --- a/tests/Unit/Install/CodeEnvironment/GeminiTest.php +++ b/tests/Unit/Install/CodeEnvironment/GeminiTest.php @@ -13,53 +13,15 @@ $this->strategyFactory = Mockery::mock(DetectionStrategyFactory::class); }); -test('name returns gemini', function (): void { - $gemini = new Gemini($this->strategyFactory); - - expect($gemini->name())->toBe('gemini'); -}); - -test('displayName returns Gemini', function (): void { - $gemini = new Gemini($this->strategyFactory); - - expect($gemini->displayName())->toBe('Gemini'); -}); - -test('systemDetectionConfig returns command detection for Darwin', function (): void { +test('systemDetectionConfig returns command-based detection for all platforms', function (): void { $gemini = new Gemini($this->strategyFactory); expect($gemini->systemDetectionConfig(Platform::Darwin)) - ->toBe(['command' => 'command -v gemini']); -}); - -test('systemDetectionConfig returns command detection for Linux', function (): void { - $gemini = new Gemini($this->strategyFactory); - - expect($gemini->systemDetectionConfig(Platform::Linux)) - ->toBe(['command' => 'command -v gemini']); -}); - -test('systemDetectionConfig returns command detection for Windows', function (): void { - $gemini = new Gemini($this->strategyFactory); - - expect($gemini->systemDetectionConfig(Platform::Windows)) - ->toBe(['command' => 'where gemini 2>nul']); -}); - -test('projectDetectionConfig returns paths and files', function (): void { - $gemini = new Gemini($this->strategyFactory); - - expect($gemini->projectDetectionConfig()) - ->toBe([ - 'paths' => ['.gemini'], - 'files' => ['GEMINI.md'], - ]); -}); - -test('mcpConfigPath returns gemini settings path', function (): void { - $gemini = new Gemini($this->strategyFactory); - - expect($gemini->mcpConfigPath())->toBe('.gemini/settings.json'); + ->toHaveKey('command') + ->and($gemini->systemDetectionConfig(Platform::Linux)) + ->toHaveKey('command') + ->and($gemini->systemDetectionConfig(Platform::Windows)) + ->toHaveKey('command'); }); test('guidelinesPath returns default GEMINI.md when no config set', function (): void { @@ -68,8 +30,8 @@ expect($gemini->guidelinesPath())->toBe('GEMINI.md'); }); -test('guidelinesPath returns custom path from config', function (): void { - config(['boost.agents.gemini.guidelines_path' => 'docs/GEMINI.md']); +test('guidelinesPath returns a custom path from config', function (): void { + config(['boost.code_environments.gemini.guidelines_path' => 'docs/GEMINI.md']); $gemini = new Gemini($this->strategyFactory); diff --git a/tests/Unit/Install/CodeEnvironment/OpenCodeTest.php b/tests/Unit/Install/CodeEnvironment/OpenCodeTest.php index eac2d3f6..c89a1714 100644 --- a/tests/Unit/Install/CodeEnvironment/OpenCodeTest.php +++ b/tests/Unit/Install/CodeEnvironment/OpenCodeTest.php @@ -6,7 +6,6 @@ use Laravel\Boost\Install\CodeEnvironment\OpenCode; use Laravel\Boost\Install\Detection\DetectionStrategyFactory; -use Laravel\Boost\Install\Enums\McpInstallationStrategy; use Laravel\Boost\Install\Enums\Platform; use Mockery; @@ -14,72 +13,15 @@ $this->strategyFactory = Mockery::mock(DetectionStrategyFactory::class); }); -test('name returns opencode', function (): void { - $opencode = new OpenCode($this->strategyFactory); - - expect($opencode->name())->toBe('opencode'); -}); - -test('displayName returns OpenCode', function (): void { - $opencode = new OpenCode($this->strategyFactory); - - expect($opencode->displayName())->toBe('OpenCode'); -}); - -test('systemDetectionConfig returns command detection for Darwin', function (): void { +test('systemDetectionConfig returns command-based detection for all platforms', function (): void { $opencode = new OpenCode($this->strategyFactory); expect($opencode->systemDetectionConfig(Platform::Darwin)) - ->toBe(['command' => 'command -v opencode']); -}); - -test('systemDetectionConfig returns command detection for Linux', function (): void { - $opencode = new OpenCode($this->strategyFactory); - - expect($opencode->systemDetectionConfig(Platform::Linux)) - ->toBe(['command' => 'command -v opencode']); -}); - -test('systemDetectionConfig returns command detection for Windows', function (): void { - $opencode = new OpenCode($this->strategyFactory); - - expect($opencode->systemDetectionConfig(Platform::Windows)) - ->toBe(['command' => 'where opencode 2>nul']); -}); - -test('projectDetectionConfig returns files', function (): void { - $opencode = new OpenCode($this->strategyFactory); - - expect($opencode->projectDetectionConfig()) - ->toBe([ - 'files' => ['AGENTS.md', 'opencode.json'], - ]); -}); - -test('mcpInstallationStrategy returns FILE', function (): void { - $opencode = new OpenCode($this->strategyFactory); - - expect($opencode->mcpInstallationStrategy()) - ->toBe(McpInstallationStrategy::FILE); -}); - -test('mcpConfigPath returns opencode.json', function (): void { - $opencode = new OpenCode($this->strategyFactory); - - expect($opencode->mcpConfigPath())->toBe('opencode.json'); -}); - -test('mcpConfigKey returns mcp', function (): void { - $opencode = new OpenCode($this->strategyFactory); - - expect($opencode->mcpConfigKey())->toBe('mcp'); -}); - -test('defaultMcpConfig returns schema', function (): void { - $opencode = new OpenCode($this->strategyFactory); - - expect($opencode->defaultMcpConfig()) - ->toBe(['$schema' => 'https://opencode.ai/config.json']); + ->toHaveKey('command') + ->and($opencode->systemDetectionConfig(Platform::Linux)) + ->toHaveKey('command') + ->and($opencode->systemDetectionConfig(Platform::Windows)) + ->toHaveKey('command'); }); test('mcpServerConfig returns correct structure', function (): void { @@ -100,8 +42,8 @@ expect($opencode->guidelinesPath())->toBe('AGENTS.md'); }); -test('guidelinesPath returns custom path from config', function (): void { - config(['boost.agents.opencode.guidelines_path' => 'docs/AGENTS.md']); +test('guidelinesPath returns a custom path from config', function (): void { + config(['boost.code_environments.opencode.guidelines_path' => 'docs/AGENTS.md']); $opencode = new OpenCode($this->strategyFactory); diff --git a/tests/Unit/Install/CodeEnvironment/PhpStormTest.php b/tests/Unit/Install/CodeEnvironment/PhpStormTest.php index 91e76ca6..c81caad3 100644 --- a/tests/Unit/Install/CodeEnvironment/PhpStormTest.php +++ b/tests/Unit/Install/CodeEnvironment/PhpStormTest.php @@ -13,85 +13,25 @@ $this->strategyFactory = Mockery::mock(DetectionStrategyFactory::class); }); -test('name returns phpstorm', function (): void { - $phpstorm = new PhpStorm($this->strategyFactory); - - expect($phpstorm->name())->toBe('phpstorm'); -}); - -test('displayName returns PhpStorm', function (): void { - $phpstorm = new PhpStorm($this->strategyFactory); - - expect($phpstorm->displayName())->toBe('PhpStorm'); -}); - -test('useAbsolutePathForMcp is true', function (): void { - $phpstorm = new PhpStorm($this->strategyFactory); - - expect($phpstorm->useAbsolutePathForMcp)->toBeTrue(); -}); - -test('systemDetectionConfig returns paths for Darwin', function (): void { +test('systemDetectionConfig returns path-based detection for all platforms', function (): void { $phpstorm = new PhpStorm($this->strategyFactory); expect($phpstorm->systemDetectionConfig(Platform::Darwin)) - ->toBe(['paths' => ['/Applications/PhpStorm.app']]); -}); - -test('systemDetectionConfig returns paths for Linux', function (): void { - $phpstorm = new PhpStorm($this->strategyFactory); - - expect($phpstorm->systemDetectionConfig(Platform::Linux)) - ->toBe([ - 'paths' => [ - '/opt/phpstorm', - '/opt/PhpStorm*', - '/usr/local/bin/phpstorm', - '~/.local/share/JetBrains/Toolbox/apps/PhpStorm/ch-*', - ], - ]); -}); - -test('systemDetectionConfig returns paths for Windows', function (): void { - $phpstorm = new PhpStorm($this->strategyFactory); - - expect($phpstorm->systemDetectionConfig(Platform::Windows)) - ->toBe([ - 'paths' => [ - '%ProgramFiles%\\JetBrains\\PhpStorm*', - '%LOCALAPPDATA%\\JetBrains\\Toolbox\\apps\\PhpStorm\\ch-*', - '%LOCALAPPDATA%\\Programs\\PhpStorm', - ], - ]); -}); - -test('projectDetectionConfig returns idea and junie paths', function (): void { - $phpstorm = new PhpStorm($this->strategyFactory); - - expect($phpstorm->projectDetectionConfig()) - ->toBe(['paths' => ['.idea', '.junie']]); -}); - -test('agentName returns Junie', function (): void { - $phpstorm = new PhpStorm($this->strategyFactory); - - expect($phpstorm->agentName())->toBe('Junie'); -}); - -test('mcpConfigPath returns junie mcp path', function (): void { - $phpstorm = new PhpStorm($this->strategyFactory); - - expect($phpstorm->mcpConfigPath())->toBe('.junie/mcp/mcp.json'); + ->toHaveKey('paths') + ->and($phpstorm->systemDetectionConfig(Platform::Linux)) + ->toHaveKey('paths') + ->and($phpstorm->systemDetectionConfig(Platform::Windows)) + ->toHaveKey('paths'); }); -test('guidelinesPath returns default path when no config set', function (): void { +test('guidelinesPath returns a default path when no config set', function (): void { $phpstorm = new PhpStorm($this->strategyFactory); expect($phpstorm->guidelinesPath())->toBe('.junie/guidelines.md'); }); -test('guidelinesPath returns custom path from config', function (): void { - config(['boost.agents.phpstorm.guidelines_path' => '.idea/guidelines.md']); +test('guidelinesPath returns a custom path from config', function (): void { + config(['boost.code_environments.phpstorm.guidelines_path' => '.idea/guidelines.md']); $phpstorm = new PhpStorm($this->strategyFactory); From 57736f437dd2f49b11c31129c8746146b879927e Mon Sep 17 00:00:00 2001 From: Pushpak Chhajed Date: Thu, 18 Dec 2025 20:36:05 +0530 Subject: [PATCH 3/3] Formatting Signed-off-by: Pushpak Chhajed --- config/boost.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/config/boost.php b/config/boost.php index 68b312d6..d99e4c65 100644 --- a/config/boost.php +++ b/config/boost.php @@ -3,6 +3,7 @@ declare(strict_types=1); return [ + /* |-------------------------------------------------------------------------- | Boost Master Switch @@ -27,4 +28,5 @@ */ 'browser_logs_watcher' => env('BOOST_BROWSER_LOGS_WATCHER', true), + ];