From 6878bf0590295ad9ecdc84a4c72a90cfc4d2d44d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucian=20V=C4=83c=C4=83roiu?= Date: Fri, 24 Oct 2025 22:50:58 +0300 Subject: [PATCH 1/2] docs: update copilot rules --- .cursor/rules/00-main.mdc | 51 ++-- .cursor/rules/01-architecture.mdc | 149 +++++----- .cursor/rules/02-tests.mdc | 98 +++---- .cursor/rules/03-commands.mdc | 439 ++++++------------------------ .cursor/rules/04-bash-style.mdc | 116 ++++++++ .cursor/rules/05-playbooks.mdc | 154 +++++++++++ .cursor/rules/rules.mdc | 165 +++++++++++ 7 files changed, 647 insertions(+), 525 deletions(-) create mode 100644 .cursor/rules/04-bash-style.mdc create mode 100644 .cursor/rules/05-playbooks.mdc create mode 100644 .cursor/rules/rules.mdc diff --git a/.cursor/rules/00-main.mdc b/.cursor/rules/00-main.mdc index 06dadf49..5fad314e 100644 --- a/.cursor/rules/00-main.mdc +++ b/.cursor/rules/00-main.mdc @@ -4,40 +4,51 @@ alwaysApply: true ## Development Rules -We're developing Deployer PHP, a Composer package and CLI tool that simplifies provisioning and setting up servers across multiple Cloud providers, enabling you to deploy your PHP projects effortlessly. +All rules MANDATORY. -**🚨 All our rules are MANDATORY and IMPORTANT!** +### Mission -**πŸ”Ž Before ANY Task:** +Build Deployer PHP: Composer package and CLI tool simplifying server provisioning and deployment across multiple Cloud providers. + +### Before ANY Task - Check `composer.json` and `package.json` for installed packages -- Plan with features from the major installed versions +- Plan with features from installed major versions - Use Context7 MCP -**πŸ«₯ Minimalist Code Philosophy:** +### Code Philosophy -- Only write the minimum amount of code necessary to solve the problem β€” no more, no less -- Always ask yourself if less code can achieve the same result -- Refactor relentlessly for code clarity and necessity +**Minimalism:** -**Performance & Efficiency Patterns:** +- Write minimum code necessary - no more, no less +- Always ask: can less code achieve same result? +- Refactor relentlessly for clarity and necessity +- Eliminate single-use methods: inline if called once +- Cache computed values: initialize expensive calculations in constructor +- Avoid method call overhead: direct property access when appropriate -- **Eliminate single-use methods**: If a private method is called only once, inline it directly -- **Cache computed values**: Initialize expensive calculations in constructor instead of repeating them -- **Avoid method call overhead**: Direct property access over method calls when appropriate +**Organization:** -**πŸ€“ Organize & Catalog Like A Librarian:** +- Catalog like a librarian +- Group related functions into comment-separated sections +- Prefer alphabetical ordering when no logical grouping exists +- Code should be functional and visually appealing -Organize and catalog code like a librarian. Group related functions into comment-separated sections for visual clarity and prefer alphabetical ordering when it doesn’t fight logical grouping. Code should be functional and visually appealing; there is beauty in order. +**Consistency:** -**πŸ€– Obsessive Code Consistency:** +- Maintain rigorous consistency across codebase +- Same style, standards, aesthetic principles throughout +- Review surrounding code for reusable patterns +- Code should appear written by single person: naming, parameter precedence, logic flow, organization -Be rigorous to the point of perfectionism in maintaining implementation consistency and logical coherence across similar areas of the codebase. Additionally, make it a habit to regularly review other code in and around the files you are working on to identify reusable patterns. +### Execution Protocol -The goal is for all the code in this repository to appear as if it were written by a single individual, adhering to a uniform style, a consistent set of standards, and aesthetic principles in software development. +1. ULTRATHINK - analyze problem deeply +2. STEP BY STEP - break into logical steps +3. ACT - implement systematically -This can include everything from naming files, classes, variables, or array keys to the precedence and type of parameters passed to a function, to how logic flows and how the code is organized or commented. +### Tests -**βœ”οΈ Tests are off-limits:** Don't run or edit tests; run or edit tests ONLY if explicitly instructed to do so! +Don't run or create or update tests UNLESS explicitly instructed to do so. -**🧠 AI Agent Protocol:** ULTRATHINK β†’ STEP BY STEP β†’ ACT +Test are something we need focus on separately from building features. diff --git a/.cursor/rules/01-architecture.mdc b/.cursor/rules/01-architecture.mdc index eeaee00e..b7e1f5a6 100644 --- a/.cursor/rules/01-architecture.mdc +++ b/.cursor/rules/01-architecture.mdc @@ -4,130 +4,109 @@ alwaysApply: true ## Architecture Rules -- **PHP:** PSR-12, strict types, PHP 8.x features (unions, match, attributes, readonly) -- **Imports:** Always add `use` statements instead of fully qualified class names -- **Type Safety:** All methods must have explicit return types with proper generics (e.g., `Collection`) +All rules MANDATORY. -**🚨 Architecture rules are IMMUTABLE - fix violating code, not the architecture rules** +### PHP Standards -### Symfony Patterns +- PSR-12, strict types, PHP 8.x features (unions, match, attributes, readonly) +- Explicit return types with generics: `Collection` +- Dependency injection via Symfony patterns +- Use Symfony classes over native PHP functions (Filesystem, Process) for testability -- **Dependency Injection:** instead of manually resolving and instantiating classes -- **Symfony Classes:** instead of native PHP functions for easier mocking during testing (eg. `Filesystem::`, `Process::`, etc.) +### Imports -## Dependency Injection System - -Use `$container->build(ClassName::class)` for all object creation instead of `new ClassName()`. - -**Core Flow:** +Always add `use` statements for vendor packages and project classes. Root namespace FQDNs acceptable (`\InvalidArgumentException`, `\RuntimeException`). ```php -// βœ… CORRECT - Auto-wires dependencies via injected container -$service = $this->container->build(MyService::class); -$command = $container->build(HelloCommand::class); +// βœ… CORRECT +use Symfony\Component\Filesystem\Filesystem; +use Bigpixelrocket\DeployerPHP\Services\IOService; -// ❌ WRONG - Manual instantiation breaks DI -$service = new MyService(new Dependency()); -``` +$fs = new Filesystem(); +throw new \InvalidArgumentException('Error'); -**How It Works:** - -1. `Container->build()` uses reflection to analyze constructor parameters -2. Recursively builds all dependencies automatically -3. Caches reflection data for performance -4. Handles circular dependencies and error cases +// ❌ WRONG - inline FQDNs for non-root namespaces +$fs = new \Symfony\Component\Filesystem\Filesystem(); +``` -**Integration Points:** +### Dependency Injection System -- Entry point: [bin/deployer](mdc:bin/deployer) β†’ Direct container instantiation and `$app->run()` -- Command registration: [SymfonyApp.php](mdc:app/SymfonyApp.php) β†’ `$this->container->build(HelloCommand::class)` -- Services: Auto-inject dependencies like `Filesystem`, `EnvService` via constructor +Use `$container->build(ClassName::class)` for all object creation. Container uses reflection to auto-wire dependencies. -**Key Benefits:** +```php +// βœ… CORRECT +$service = $this->container->build(MyService::class); -- Zero configuration - pure PHP reflection -- Type-safe with generics support -- Automatic error detection and meaningful messages -- Easy testing with mockable dependencies -- No manual dependency wiring required +// ❌ WRONG +$service = new MyService(new Dependency()); +``` -**Rule:** ALL object creation must use `$container->build()` except for value objects, DTOs, and pure data structures. +**Rule:** ALL object creation uses `$container->build()` except DTOs, value objects, pure data structures. -**Container Access:** In production code, access the container through constructor injection. +**Container Access:** Constructor injection in production, direct instantiation in tests. ```php -// βœ… PRODUCTION CODE - Container injected via DI +// Production class SymfonyApp { - public function __construct(private readonly Container $container) { ... } - + public function __construct(private readonly Container $container) {} private function registerCommands(): void { $command = $this->container->build(HelloCommand::class); } } -// βœ… TESTS - Direct container instantiation for isolation +// Tests $container = new Container(); $service = $container->build(TestService::class); ``` -**Test Mocking:** The Container supports `bind()` for registering mock instances in tests. +**Test Mocking:** Container supports `bind()` for mock instances: ```php -// Register mocks for testing $container = new Container(); $container->bind(SSHService::class, $mockSSH); -$container->bind(EnvService::class, $mockEnv); - -// Container uses bound instances instead of auto-wiring -$command = $container->build(ServerAddCommand::class); // Gets mocks +$command = $container->build(ServerAddCommand::class); // Gets mock ``` -This enables isolated testing while maintaining production auto-wiring behavior. +**Integration:** Entry point: bin/deployer. Command registration: SymfonyApp.php. Services: Auto-injected via constructor. -### Command Layer +### Layer Separation -- Commands handle user interaction (input/output) and orchestrate Services -- Commands must NOT contain business logic - delegate to Services -- Commands must NOT duplicate orchestration logic - extract to shared Services -- Commands are responsible for console styling, error formatting, and user prompts -- Commands should not invoke other commands - NO proxy commands +**Command Layer:** -### Service Layer (Business Logic) +- Handle user interaction (input/output), orchestrate Services +- NO business logic (delegate to Services) +- NO duplicate orchestration (extract to shared Services) +- Responsible for console styling, error formatting, prompts +- Never invoke other commands (NO proxy commands) -- Services provide atomic, reusable functionality with no console I/O -- Services accept plain PHP data types and return plain PHP data types -- Services must be dependency-injected via constructor -- Services handle core business logic, external API calls, file operations -- Complex orchestration shared by multiple Commands should be extracted to dedicated Services +**Service Layer:** -**Service State:** +- Atomic, reusable functionality with NO console I/O +- Accept/return plain PHP data types +- Dependency-injected via constructor +- Handle business logic, external APIs, file operations +- Complex orchestration shared by Commands extracted to dedicated Services -- **Stateless Services:** Pure operations with no internal state (e.g., validators, calculators, API clients) -- **Stateful Services:** Services that manage configuration or cached data (e.g., config loaders, file managers, repositories) -- Stateful services should use lazy loading when initialization is expensive or path-dependent -- State must be initialized explicitly via public methods before use (e.g., `load()`, `initialize()`) -- Services should document their stateful nature and initialization requirements +**Service State:** -### Console I/O Rules +- Stateless: Pure operations, no internal state (validators, calculators, API clients) +- Stateful: Manage configuration/cached data (config loaders, file managers, repositories) +- Stateful services use lazy loading, explicit initialization via public methods (`load()`, `initialize()`), document requirements -- Only Commands perform console input/output operations -- Use SymfonyStyle consistently for all user-facing output -- Services return exceptions or structured data for Commands to handle -- Validation errors and business exceptions bubble up to Commands for display +**Console I/O:** Only Commands perform console I/O. SymfonyStyle for all output. Services return exceptions/data for Commands to display. See 03-commands.mdc. -### Dependency Rules +**Dependencies:** - Commands depend on Services -- Services depend on other Services or utilities -- All dependencies declared in constructor signatures - -**IMPORTANT:** No circular dependencies allowed. +- Services depend on Services/utilities +- All dependencies in constructor signatures +- NO circular dependencies ### Comments -**DocBlock:** Add docblock comments with minimalist descriptions, parameters and return types for classes and functions; +**DocBlock:** Minimalist descriptions, parameters, return types for classes and functions. -**Comment everything:** Use comments to separate sections and explain or summarize complex logic; +**Comment structure:** ``` // @@ -140,20 +119,16 @@ This enables isolated testing while maintaining production auto-wiring behavior. // {Paragraph} ``` -- Use comments as visual separators to help separate different sections -- Separate section headers, subheaders and paragraphs with a single newline -- Avoid commenting the obvious or leaving comments behind when removing code +Separate sections visually. One newline between headers/subheaders/paragraphs. No obvious comments. Remove comments when removing code. ### Quality Gates -**ALWAYS run these commands against the files you have touched and fix any issues BEFORE considering a task complete:** +ALWAYS run before completing task, fix all issues: ```bash -vendor/bin/rector $CHANGED_PHP_FILES # Code improvements (changed files only) -vendor/bin/pint $CHANGED_PHP_FILES # Fix code style (changed files only) - -# Static analysis excluding tests (never do static analysis against tests) -vendor/bin/phpstan analyze $CHANGED_PHP_FILES_EXCEPT_TESTS +vendor/bin/rector $CHANGED_PHP_FILES # Code improvements +vendor/bin/pint $CHANGED_PHP_FILES # Fix style +vendor/bin/phpstan analyze $CHANGED_PHP_FILES_EXCEPT_TESTS # NEVER run on tests/ ``` -**Important: ** Don't run PHPStan on test files; tests are excluded from static analysis. +PHPStan excluded from tests - tests focus on testing functionality over type compliance. diff --git a/.cursor/rules/02-tests.mdc b/.cursor/rules/02-tests.mdc index ed3eee44..3d7a3940 100644 --- a/.cursor/rules/02-tests.mdc +++ b/.cursor/rules/02-tests.mdc @@ -4,144 +4,133 @@ alwaysApply: true ## Testing Rules +All rules MANDATORY. + **Philosophy:** "A test that never fails is not a test, it's a lie." -**Framework:** Pest exclusively with `it()` syntax, 80%+ coverage +**Framework:** Pest exclusively, `it()` syntax, 80%+ coverage ### Running Tests -- `composer pest` - run entire test suite in parallel, with coverage -- `vendor/bin/pest $TEST_FILE` - run specific test file +```bash +composer pest # Full suite with coverage (parallel) +vendor/bin/pest $TEST_FILE # Specific file +``` ### Dependency Injection in Tests -**The DI Container rule applies to PRODUCTION code, not tests.** +**DI Container rule applies to PRODUCTION code, not tests.** -**βœ… PREFERRED - Manual Instantiation (Unit Tests):** +**Unit Tests (Services/Utilities) - Manual Instantiation:** ```php -// Testing services in isolation $mockFs = mockFilesystem(true, 'content'); $service = new EnvService(new FilesystemService($mockFs), new Dotenv()); ``` -Benefits: Clear dependency wiring, easy mock injection, no container overhead. +Clear dependency wiring, easy mocking, no container overhead. -**βœ… RECOMMENDED - Container with Bindings (Command/Integration Tests):** +**Command/Integration Tests - Container with Bindings:** ```php -// Testing commands with mockCommandContainer() +// Basic usage $container = mockCommandContainer(); $command = $container->build(ServerAddCommand::class); -// Override specific services when needed +// Override services $customSSH = mockSSHServiceWithBehavior(canConnect: false); $container = mockCommandContainer(ssh: $customSSH); -$command = $container->build(ServerAddCommand::class); -// Pre-populate inventory data +// Pre-populate data $container = mockCommandContainer( inventoryData: ['servers' => [['name' => 'web1', 'host' => '192.168.1.1']]] ); -$command = $container->build(ServerListCommand::class); ``` -Benefits: Sustainable (no updates needed when BaseCommand grows), consistent pattern, easy service overrides. +Sustainable pattern - no updates when BaseCommand grows. -**βœ… OPTIONAL - Container Auto-wiring (Edge Cases):** +**Container Auto-wiring (Edge Cases Only):** ```php -// Testing DI configuration or service integration $container = new Container(); $container->bind(Filesystem::class, $mockFs); $service = $container->build(CustomService::class); ``` -When to use: Verifying DI configuration or testing multiple services together. - -**Rule:** Unit tests (services/utilities) use manual instantiation. Command/integration tests use `mockCommandContainer()`. +Use when verifying DI configuration or testing service integration. -**Maintenance Note:** When adding a new service to `BaseCommand`, update `mockCommandContainer()` in `tests/TestHelpers.php`: +**Maintenance:** When adding service to BaseCommand, update `mockCommandContainer()` in `tests/TestHelpers.php`: ```php function mockCommandContainer( ?NewService $newService = null, // 1. Add parameter - // ... existing parameters + // ... existing params ) { - // ... $newService = $newService ?? mockNewService(); // 2. Build or use provided - - // Bind services to container - $container->bind(NewService::class, $newService); // 3. Bind to container - // ... + $container->bind(NewService::class, $newService); // 3. Bind } ``` -This is the ONLY place you need to update for command testing. - ### Test Minimalism -**Target:** Keep test files under 1.8x the size of source code they test. +**Target:** Keep test files under 1.8x source code size. **Rules:** - Test core business logic only, skip framework testing -- Use dataset-driven testing: `->with([])` for multiple scenarios -- Eliminate test overlap: no two tests covering same functionality +- Use datasets: `->with([])` for multiple scenarios +- Eliminate overlap: no two tests covering same functionality - Consolidate assertions: `expect($x)->toBe(1)->and($y)->toBe(2)` - Mock external dependencies only -- No performance tests unless performance is the primary concern +- No performance tests unless performance is primary concern - Don't sacrifice readability for ratio targets -**Don't consolidate when:** +**Don't consolidate:** Different public methods, exception vs normal flow, different setup, distinct business logic. -- Different public methods -- Exception vs normal flow tests -- Different setup requirements -- Distinct business logic - -### AAA Pattern (MANDATORY) +### AAA Pattern ```php -it('does something specific', function () { +it('does something', function () { // ARRANGE $service = new Service(mock(Dependency::class)); // ACT - $result = $service->performAction(); + $result = $service->action(); // ASSERT expect($result)->toBe('expected'); // CLEANUP (when needed) - $this->resetTimeState(); unlink($tempFile); }); ``` -**Exception tests:** Use `// ACT & ASSERT` when act triggers assertion. +Exception tests: `// ACT & ASSERT` when act triggers assertion. -**Organization:** Use `describe()` blocks, `beforeEach()` setup, extract helpers/traits for DRY tests. +Use `describe()` blocks, `beforeEach()` setup, extract helpers/traits for DRY tests. ### Testing Patterns -**❌ FORBIDDEN:** +**Forbidden:** ```php -expect($x)->toBeInstanceOf(Class::class); // Type-only testing -expect($x)->toBeArray(); // Generic assertions -expect($x)->not->toBeNull(); // Meaningless on its own -expect($x)->not->toBeNull()->and($x)->toContain('text'); // Redundant (toContain already guarantees non-null) -expect(true)->toBeTrue(); // Literally meaningless -sleep(...); // Use time mocking +expect($x)->toBeInstanceOf(Class::class); // Type-only testing +expect($x)->toBeArray(); // Generic assertion +expect($x)->not->toBeNull(); // Meaningless alone +expect($x)->not->toBeNull()->and($x)->toContain('text'); // Redundant +expect(true)->toBeTrue(); // Literally meaningless +sleep(...); // Test logic not time ``` -**βœ… REQUIRED:** +**Required:** ```php expect($config->getValue('host'))->toBe('example.com'); expect($this->validator->isValid($input))->toBe($expected); $mock->shouldReceive('method')->with('param')->andReturn('result'); + +// For polling/timeout - use zero intervals +$service->waitForReady('id', timeout: 10, pollInterval: 0); ``` ### Test Types @@ -165,7 +154,6 @@ $mock->shouldReceive('method')->with('param')->andReturn('result'); ### Static Analysis -**Running PHPStan applies to PRODUCTION code, not tests.** +**PHPStan applies to PRODUCTION code, not tests.** -- Ignore PHPStan issues in tests - focus on test functionality over compliance -- Avoid excessive phpdoc just to appease types +Ignore PHPStan issues in tests. Focus on functionality over compliance. Avoid excessive phpdoc to appease types. diff --git a/.cursor/rules/03-commands.mdc b/.cursor/rules/03-commands.mdc index f6f0bfa6..c23a99f8 100644 --- a/.cursor/rules/03-commands.mdc +++ b/.cursor/rules/03-commands.mdc @@ -4,214 +4,77 @@ alwaysApply: true ## Symfony Console Rules -**🚨 Console rules are MANDATORY and IMMUTABLE - fix violating code, not the console rules** +All rules MANDATORY. Fix violating code, not these rules. -### πŸ“€ Output Method Philosophy +### Output Method Philosophy -**🚨 NEVER use Symfony IO methods directly - use [BaseCommand.php](mdc:app/Contracts/BaseCommand.php) methods exclusively** +NEVER use Symfony IO methods directly - use BaseCommand methods exclusively. -**Core Principle:** All console output flows through custom methods in BaseCommand that: +All console output flows through custom methods in BaseCommand for consistent TUI styling. -- Provide modern, beautiful TUI styling -- Maintain consistent user experience -- Enable centralized output control - -**Custom IO Methods (MANDATORY):** +**Custom IO Methods:** ```php -// βœ… CORRECT - BaseCommand custom methods -$this->writeln(['Multiple', 'lines']); // Multi-line output -$this->hr(); // Beautiful section separator -$this->h1('Section Heading'); // Heading with icon +// Output +$this->writeln(['Multiple', 'lines']); +$this->io->hr(); +$this->io->h1('Section Heading'); // Status messages -$this->success('Server added successfully'); -$this->error('Failed to connect to server'); -$this->warning('Skipping connection check'); -$this->info('Configuration loaded'); +$this->success('Operation completed'); // Green checkmark +$this->error('Operation failed'); // Red X +$this->warning('Skipping step'); // Yellow warning +$this->info('Configuration loaded'); // Cyan info ``` -**❌ FORBIDDEN - Direct Symfony IO Usage:** - -```php -// ❌ NEVER use Symfony methods directly -$this->io->writeln('Direct output'); // Bypasses our custom styling -$this->io->text('Raw text'); // Inconsistent with our TUI -$this->io->success('Task done'); // Basic, outdated styling -``` - -**πŸ”§ Missing a Method? Create It!** - -If you need output functionality not in [BaseCommand.php](mdc:app/Contracts/BaseCommand.php): - -1. Add a new method to BaseCommand -2. Use modern styling and formatting -3. Keep it reusable and minimal -4. Document with example usage - -```php -// βœ… CORRECT - Extend BaseCommand for new needs -protected function success(string $message): void { - // Custom success styling here -} -``` +**Missing a method?** Add to BaseCommand with modern styling. **Integration Points:** -- Base implementation: [BaseCommand.php](mdc:app/Contracts/BaseCommand.php) -- Output trait: [ConsoleOutputTrait.php](mdc:app/Traits/ConsoleOutputTrait.php) -- Input trait: [ConsoleInputTrait.php](mdc:app/Traits/ConsoleInputTrait.php) -- Output methods: `writeln()`, `info()`, `hr()`, `h1()` -- Status methods: `success()`, `error()`, `warning()` -- Input methods: `getOptionOrPrompt()`, `getValidatedOptionOrPrompt()`, `promptText()`, `promptPassword()`, `promptConfirm()`, `promptSelect()`, `promptMultiselect()`, `promptSuggest()`, `promptSearch()`, `promptPause()`, `promptSpin()` -- Helper methods: `showCommandHint()` - -**When to Add New Methods:** - -Console functionality is organized using traits for reusability: - -- **ConsoleOutputTrait:** Add output/formatting methods that format and display text - - Examples: status messages, headings, separators, formatting helpers - - All methods should work with `$this->io` (SymfonyStyle) +- Base: BaseCommand.php +- Output: ConsoleOutputTrait.php +- Input: ConsoleInputTrait.php +- Methods: `writeln()`, `info()`, `hr()`, `h1()`, `success()`, `error()`, `warning()`, `getOptionOrPrompt()`, `getValidatedOptionOrPrompt()`, `promptText()`, `promptPassword()`, `promptConfirm()`, `promptSelect()`, `promptMultiselect()`, `promptSuggest()`, `promptSearch()`, `promptPause()`, `promptSpin()`, `showCommandHint()` -- **ConsoleInputTrait:** Add input gathering methods that collect user data - - Examples: prompt helpers, option validators, input transformers - - All methods should work with `$this->input` (InputInterface) +**Trait Organization:** -- **BaseCommand:** Add only shared initialization, configuration, or orchestration logic - - Examples: service initialization, common options, execution flow - - NOT for individual I/O operations +- ConsoleOutputTrait: Output/formatting methods using `$this->io` (SymfonyStyle) +- ConsoleInputTrait: Input methods using `$this->input` (InputInterface) +- BaseCommand: Shared initialization, configuration, orchestration (NOT individual I/O ops) -This separation ensures console I/O methods remain reusable across different command contexts. +### User Input with Laravel Prompts -### πŸ“£ Status Message Helpers - -**Use status helpers for consistent success/error/warning messages:** +Use `laravel/prompts` for ALL user interactions: ```php -// βœ… Success messages (green checkmark) -$this->success('Server added successfully'); -$this->success('Connection successful'); - -// βœ… Error messages (red X) -$this->error('Failed to connect to server'); -$this->error('Failed to add server: ' . $e->getMessage()); - -// βœ… Info messages (cyan info symbol) -$this->info('Configuration loaded from .env'); -$this->info('Using custom inventory path'); - -// βœ… Warning messages (yellow warning symbol) -$this->warning('Skipping SSH connection check'); -$this->warning('Server add cancelled'); -``` - -**Benefits:** - -- Consistent formatting across all commands -- Clear visual indicators (βœ“, βœ—, β„Ή, ⚠) -- Modern, colorful output styling +use function Laravel\Prompts\{text, password, confirm, select, multiselect, suggest, search, spin}; -### 🎯 User Input with Laravel Prompts - -**Use `laravel/prompts` for ALL user interactions to create rich CLI experiences:** - -```php -use function Laravel\Prompts\text; -use function Laravel\Prompts\password; -use function Laravel\Prompts\confirm; -use function Laravel\Prompts\select; -use function Laravel\Prompts\multiselect; -use function Laravel\Prompts\suggest; -use function Laravel\Prompts\search; -use function Laravel\Prompts\spin; - -// βœ… CORRECT - Modern prompts $name = text('What is your name?', required: true); $password = password('Enter password:', required: true); -$confirmed = confirm('Deploy to production?', default: false); -$environment = select('Select environment:', ['dev', 'staging', 'prod']); -$features = multiselect('Enable features:', ['cache', 'queue', 'logs']); - -// Spinners for long operations -$result = spin(fn() => $this->service->heavyOperation(), 'Processing...'); -``` - -**Key Benefits:** - -- Beautiful, interactive prompts with validation -- Auto-complete and search functionality -- Loading spinners for long operations -- Consistent, modern user experience -- Zero Symfony IO boilerplate - -### πŸ“‹ Command Layer Patterns - -**Console I/O Rules:** - -- Commands handle ALL user interaction (input/output) -- Services return plain data - NO console operations -- Use consistent styling patterns across all commands -- Validation errors bubble up to Commands for display - -**Core Flow:** - -```php -class MyCommand extends BaseCommand { - protected function execute(InputInterface $input, OutputInterface $output): int { - // βœ… CORRECT - Custom BaseCommand methods - $this->h1('Deployment'); - $this->info('Starting deployment process...'); - - // Prompt user with Laravel Prompts - $confirmed = confirm('Continue with deployment?', default: true); - - // Services return data, not output - $result = $this->service->performWork(); - - // Custom output formatting - $this->success('Deployment completed: ' . $result); - - return Command::SUCCESS; - } -} +$confirmed = confirm('Deploy?', default: false); +$env = select('Environment:', ['dev', 'staging', 'prod']); +$features = multiselect('Features:', ['cache', 'queue', 'logs']); +$result = spin(fn() => $this->service->process(), 'Processing...'); ``` -**Key Benefits:** - -- Consistent user experience across all commands -- Easy testing with mockable I/O patterns -- Clean separation between business logic and presentation - -**Rule:** Commands orchestrate Services and format output. Services never touch console I/O. +Commands handle ALL user interaction. Services return plain data with NO console operations. -### πŸ”€ Interactive + Options Pattern +### Interactive + Options Pattern -**Support both interactive prompts AND command-line options for maximum flexibility using the `getOptionOrPrompt()` helper.** +Support both interactive prompts AND CLI options using `getOptionOrPrompt()`. -**The Pattern:** - -The `getOptionOrPrompt()` method enables dual-mode commands that work both interactively and via CLI options: - -- **Signature:** `getOptionOrPrompt(string $optionName, Closure $promptCallback): mixed` -- Checks if option was provided via CLI β†’ returns option value -- If not provided β†’ executes the closure to prompt user interactively -- Supports any return type (string, bool, int, array) via the closure -- Works with all prompt methods: `promptText()`, `promptSelect()`, `promptConfirm()`, etc. - -**Example (Creating New Resource):** +**Pattern:** ```php protected function configure(): void { parent::configure(); - $this->addOption('name', null, InputOption::VALUE_REQUIRED, 'Server name'); $this->addOption('host', null, InputOption::VALUE_REQUIRED, 'Host/IP address'); $this->addOption('yes', 'y', InputOption::VALUE_NONE, 'Skip confirmation'); } protected function execute(InputInterface $input, OutputInterface $output): int { - // Text input with option fallback + // Checks CLI option first, prompts if not provided $name = $this->getOptionOrPrompt( 'name', fn() => $this->promptText( @@ -223,20 +86,12 @@ protected function execute(InputInterface $input, OutputInterface $output): int $host = $this->getOptionOrPrompt( 'host', - fn() => $this->promptText( - label: 'Host/IP address:', - placeholder: '192.168.1.100', - required: true - ) + fn() => $this->promptText('Host/IP:', placeholder: '192.168.1.100', required: true) ); - // Boolean flag with interactive fallback $skipConfirm = $this->getOptionOrPrompt( 'yes', - fn() => $this->promptConfirm( - label: 'Skip verification?', - default: false - ) + fn() => $this->promptConfirm('Skip verification?', default: false) ); // ... process command ... @@ -245,66 +100,18 @@ protected function execute(InputInterface $input, OutputInterface $output): int } ``` -**Example (Selecting Existing Resource):** +**Signature:** `getOptionOrPrompt(string $optionName, Closure $promptCallback): mixed` -```php -protected function configure(): void { - parent::configure(); +Prompt wrappers (`promptText()`, `promptSelect()`, etc.) automatically suppress extra spacing for clean output. - $this->addOption('server', null, InputOption::VALUE_REQUIRED, 'Server name'); - $this->addOption('yes', 'y', InputOption::VALUE_NONE, 'Skip confirmation'); -} +### Input Validation Pattern -protected function execute(InputInterface $input, OutputInterface $output): int { - // Selection with option fallback - $serverName = $this->getOptionOrPrompt( - 'server', - fn() => $this->promptSelect( - label: 'Select server:', - options: $this->servers->getNames() - ) - ); - - // ... process command ... - - return Command::SUCCESS; -} -``` - -**Prompt Wrapper Methods:** - -ConsoleInputTrait provides wrappers for all Laravel Prompts functions: - -- `promptText()` - Text input with validation -- `promptPassword()` - Password input (masked) -- `promptConfirm()` - Yes/No confirmation -- `promptSelect()` - Single selection from list -- `promptMultiselect()` - Multiple selections -- `promptSuggest()` - Autocomplete suggestions -- `promptSearch()` - Searchable options -- `promptPause()` - Wait for Enter key -- `promptSpin()` - Loading spinner for operations - -All wrappers automatically suppress extra spacing for cleaner output. - -**Benefits:** - -- Script-friendly with full option support -- User-friendly with interactive fallbacks -- Flexible - supports any prompt type via closures -- Type-safe - preserves return types from prompts -- DRY - single command serves both use cases +Validation traits provide reusable validation for both Laravel Prompts and CLI options. -### 🎯 Input Validation Pattern - -Validation traits provide reusable validation logic that integrates with both Laravel Prompts and CLI options. - -**Core Pattern:** - -Validation methods accept `mixed`, check type first, then return `?string` (error message or `null` if valid): +**Validation Method Pattern:** ```php -// βœ… CORRECT - Accept mixed with type guard +// βœ… CORRECT - Accept mixed, return ?string (error message or null) protected function validateNameInput(mixed $name): ?string { if (!is_string($name)) { @@ -315,33 +122,23 @@ protected function validateNameInput(mixed $name): ?string return 'Server name cannot be empty'; } - // Check uniqueness - $existing = $this->servers->findByName($name); - if ($existing !== null) { + if ($this->servers->findByName($name) !== null) { return "Server '{$name}' already exists"; } return null; } -// ❌ WRONG - Throws exception (not compatible with Laravel Prompts) +// ❌ WRONG - Throws exception (incompatible with Laravel Prompts) protected function validateName(string $name): void { if (trim($name) === '') { throw new \InvalidArgumentException('Name cannot be empty'); } } - -// ❌ WRONG - Type-hinted as string (not PHPStan-compliant) -protected function validateNameInput(string $name): ?string -{ - // ... -} ``` -**Using Validated Inputs:** - -Use `getValidatedOptionOrPrompt()` for inputs that require validation: +**Usage:** ```php $name = $this->getValidatedOptionOrPrompt( @@ -355,145 +152,66 @@ $name = $this->getValidatedOptionOrPrompt( ); if ($name === null) { - return Command::FAILURE; + return Command::FAILURE; // Validation failed, error already displayed } ``` -**How It Works:** - -1. Validator is passed only once (third parameter) -2. Method automatically injects validator into prompt callback -3. Prompts validate interactively as user types -4. CLI options are validated after retrieval -5. Returns `null` on validation failure (error already displayed) -6. Returns validated value on success +Validator injected into prompt callback, validates interactively and on CLI options. **Naming Convention:** -- Validation methods: `validate*Input()` (returns `?string`) -- Exception-throwing validators: `validate*()` (for heavy I/O operations like git repo checks) - -**Examples:** - -- [ServerValidationTrait.php](mdc:app/Traits/ServerValidationTrait.php) - `validateHostInput()`, `validatePortInput()`, `validateNameInput()` -- [SiteValidationTrait.php](mdc:app/Traits/SiteValidationTrait.php) - `validateDomainInput()`, `validateBranchInput()` - -**When to Use Exceptions:** +- `validate*Input()` - Returns `?string` (for prompts/options) +- `validate*()` - Throws exceptions (for heavy I/O like git repo checks) -For validation that involves heavy I/O operations (network calls, external processes), use exception-throwing methods: - -```php -// Heavy I/O operation - throw exceptions -protected function validateGitRepo(string $repo): void -{ - $process = $this->proc->run(['git', 'ls-remote', '--exit-code', $repo]); - - if (!$process->isSuccessful()) { - throw new \RuntimeException("Cannot access git repository '{$repo}'"); - } -} - -// Used in commands with try-catch for user-friendly error display -try { - $this->validateGitRepo($repo); - $this->success('Git repository is accessible'); -} catch (\RuntimeException $e) { - $this->error($e->getMessage()); - return Command::FAILURE; -} -``` +**Examples:** ServerValidationTrait.php, SiteValidationTrait.php **Testing Validation Traits:** -Create a test fixture class that uses the trait and exposes methods: - ```php -class TestServerValidator -{ - use ServerValidationTrait; - - public function testValidateHost(mixed $host): ?string - { +class TestValidator { use ServerValidationTrait; + public function testValidateHost(mixed $host): ?string { return $this->validateHostInput($host); } } -// Test valid inputs return null expect($validator->testValidateHost('192.168.1.100'))->toBeNull(); - -// Test invalid inputs return error messages expect($validator->testValidateHost('invalid'))->toContain('valid'); ``` -**Benefits:** - -- **Testable:** Easy to unit test without exception handling -- **Reusable:** Same method works for interactive prompts AND CLI options -- **User-friendly:** Integrates with Laravel Prompts' `validate` callback -- **Consistent:** Uniform pattern across all validation logic -- **Type-safe:** PHPStan compliant with proper type variance - -**See also:** "Command Options & Input" section below for mandatory naming conventions. - -### βš™οΈ Command Options & Input (MANDATORY) +### Command Options & Input **Naming Convention Rules:** -Command options must follow consistent naming to prevent conflicts and improve clarity: - -| Option | Usage | InputOption Type | When to Use | -| -------------- | ------------------------ | ---------------- | ----------------------------- | -| `--server` | Select existing server | `VALUE_REQUIRED` | delete, info, deploy commands | -| `--site` | Select existing site | `VALUE_REQUIRED` | site management commands | -| `--name` | Define new resource name | `VALUE_REQUIRED` | add, create commands | -| `--host` | Host/IP address | `VALUE_REQUIRED` | server configuration | -| `--port` | Port number | `VALUE_REQUIRED` | server configuration | -| `--yes` / `-y` | Skip confirmation | `VALUE_NONE` | all confirmation prompts | -| `--skip` | Skip validation | `VALUE_NONE` | validation steps | +| Option | Usage | Type | When | +| -------------- | ------------------------ | ---------------- | -------------------- | +| `--server` | Select existing server | `VALUE_REQUIRED` | delete, info, deploy | +| `--site` | Select existing site | `VALUE_REQUIRED` | site operations | +| `--name` | Define new resource name | `VALUE_REQUIRED` | add, create | +| `--host` | Host/IP address | `VALUE_REQUIRED` | server config | +| `--port` | Port number | `VALUE_REQUIRED` | server config | +| `--yes` / `-y` | Skip confirmation | `VALUE_NONE` | all confirmations | +| `--skip` | Skip validation | `VALUE_NONE` | validation steps | -**The Golden Rule:** +**Golden Rule:** -- **`--server` / `--site`**: SELECTING existing resources (resource is the operation target) -- **`--name`**: DEFINING new resource properties (name is one of many properties being set) +- `--server` / `--site`: SELECTING existing (operation target) +- `--name`: DEFINING new resource property -This distinction prevents naming conflicts in commands that work with multiple resource types. - -**Examples:** - -```php -// βœ… CORRECT - Selecting existing server (target of operation) -// Commands: server:delete, server:info, deploy, etc. -protected function configure(): void { - $this->addOption('server', null, InputOption::VALUE_REQUIRED, 'Server name'); - $this->addOption('yes', 'y', InputOption::VALUE_NONE, 'Skip confirmation'); -} - -// βœ… CORRECT - Creating new server (name is a property) -// Commands: server:add, server:create -protected function configure(): void { - $this->addOption('name', null, InputOption::VALUE_REQUIRED, 'Server name'); - $this->addOption('host', null, InputOption::VALUE_REQUIRED, 'Host/IP'); - $this->addOption('port', null, InputOption::VALUE_REQUIRED, 'Port'); - $this->addOption('skip', null, InputOption::VALUE_NONE, 'Skip SSH validation'); -} -``` +Prevents conflicts in commands working with multiple resource types. **Additional Rules:** -- Use **OPTIONS** only, never ARGUMENTS (enables `getOptionOrPrompt()` pattern) +- Use OPTIONS only, never ARGUMENTS (enables `getOptionOrPrompt()`) - Pair all options with `getOptionOrPrompt()` for dual-mode support -- Boolean flags always use `VALUE_NONE` -- Data inputs always use `VALUE_REQUIRED` -- Only `--yes` gets a short flag (`-y`) - -**See Examples:** +- Boolean flags: `VALUE_NONE` +- Data inputs: `VALUE_REQUIRED` +- Only `--yes` gets short flag (`-y`) -- Selection: [ServerDeleteCommand.php](mdc:app/Console/Server/ServerDeleteCommand.php#L32-L33) -- Creation: [ServerAddCommand.php](mdc:app/Console/Server/ServerAddCommand.php#L37-L43) +See: ServerDeleteCommand.php, ServerAddCommand.php -### 🎯 Command Completion (MANDATORY) +### Command Completion -**Always call `showCommandHint()` before returning SUCCESS to educate users on non-interactive usage:** +Always call `showCommandHint()` before returning SUCCESS to teach non-interactive usage: ```php $this->showCommandHint('command:name', [ @@ -504,14 +222,9 @@ $this->showCommandHint('command:name', [ return Command::SUCCESS; ``` -**Why This Matters:** - -- Teaches users the CLI syntax for scripting/automation -- Improves developer experience with copy-paste ready commands -- Reduces support questions about non-interactive usage -- Self-documenting command behavior +Teaches CLI syntax, improves DX, reduces support questions. -**Example Output:** +**Output:** ``` β—† Run non-interactively: @@ -521,4 +234,4 @@ return Command::SUCCESS; --yes ``` -**See:** [ServerDeleteCommand.php](mdc:app/Console/Server/ServerDeleteCommand.php#L90-L93) +See: ServerDeleteCommand.php diff --git a/.cursor/rules/04-bash-style.mdc b/.cursor/rules/04-bash-style.mdc new file mode 100644 index 00000000..59a56557 --- /dev/null +++ b/.cursor/rules/04-bash-style.mdc @@ -0,0 +1,116 @@ +--- +alwaysApply: true +--- + +## Bash Style + +All rules MANDATORY. Based on https://style.ysap.sh/md + +### Core Syntax + +**Conditionals:** Use `[[ ... ]]` not `[ ... ]` or `test` + +```bash +[[ -d /etc ]] # βœ… CORRECT +[ -d /etc ] # ❌ WRONG +``` + +**Command Substitution:** Use `$(...)` not backticks + +```bash +foo=$(date) # βœ… CORRECT +foo=`date` # ❌ WRONG +``` + +**Math:** Use `((...))` and `$((...))`, never `let` + +```bash +if ((a > b)); then ... # βœ… CORRECT +if [[ $a -gt $b ]]; then # ❌ WRONG - use math syntax for comparisons +``` + +**Functions:** No `function` keyword, always use `local` for variables + +```bash +foo() { local i=5; } # βœ… CORRECT +function foo { i=5; } # ❌ WRONG - global variable, function keyword +``` + +**Block Statements:** `then` same line as `if`, `do` same line as `while` + +```bash +if true; then ... # βœ… CORRECT +while true; do ... # βœ… CORRECT +``` + +### Parameter Handling + +**Expansion:** Prefer parameter expansion over external commands + +```bash +prog=${0##*/} # βœ… CORRECT - basename +nonumbers=${name//[0-9]/} # βœ… CORRECT - remove numbers +prog=$(basename "$0") # ❌ WRONG - external command +``` + +**Quoting:** Double quotes for expansions, single for literals + +```bash +echo "$foo" # βœ… CORRECT - expansion needs quotes +bar='literal' # βœ… CORRECT - no expansion +if [[ -n $foo ]]; then # βœ… CORRECT - [[ ... ]] doesn't word-split +``` + +Exception: Variables controlled by script (not user input) may be unquoted in `[[ ... ]]` + +**Arrays:** Use bash arrays, not space-separated strings + +```bash +modules=(a b c) # βœ… CORRECT +for m in "${modules[@]}" # βœ… CORRECT - quoted array expansion +modules='a b c' # ❌ WRONG - string not array +``` + +### Error Handling + +**Check commands that can fail:** + +```bash +cd /path || exit # βœ… CORRECT - exit on failure +cd /path # ❌ WRONG - what if cd fails? +rm file +``` + +**Pipeline errors:** Use `set -o pipefail` in playbooks + +**Don't use `set -e`:** Explicit error checking preferred over errexit + +**Never use `eval`:** Security risk, static analysis impossible + +### File Operations + +**Reading files:** Use redirection or built-in read + +```bash +while IFS=: read -r user _; do + echo "$user" +done < /etc/passwd # βœ… CORRECT - streaming + +grep foo file # βœ… CORRECT +cat file | grep foo # ❌ WRONG - useless use of cat +``` + +**Listing files:** Never parse `ls`, use globs + +```bash +for f in *; do ... # βœ… CORRECT +for f in $(ls); do ... # ❌ WRONG - unsafe +``` + +### Formatting + +- Tabs for indentation +- Max 80 columns +- Semicolons only in control statements (`if true; then`) +- Max 1 blank line between sections +- Shebang: `#!/usr/bin/env bash` diff --git a/.cursor/rules/05-playbooks.mdc b/.cursor/rules/05-playbooks.mdc new file mode 100644 index 00000000..3c09258e --- /dev/null +++ b/.cursor/rules/05-playbooks.mdc @@ -0,0 +1,154 @@ +--- +alwaysApply: true +--- + +## Playbook Rules + +All rules MANDATORY. + +### Core Principles + +Playbooks are idempotent, non-interactive bash scripts that: + +- Execute one or more related tasks +- MUST be idempotent (safe to run multiple times) +- Receive context via environment variables +- Never prompt for user input +- Run completely unattended + +### Non-Interactive Operation + +All playbooks MUST run without user interaction: + +- Set `DEBIAN_FRONTEND=noninteractive` for Debian/Ubuntu +- Use `-y` flag for package managers (apt-get, yum) +- Use `-q` flag to suppress unnecessary output +- Use `--batch --yes` for GPG operations +- Use `--quiet` for systemctl where appropriate +- Never use `read`, confirm dialogs, or interactive prompts + +### Environment Variables + +Use `DEPLOYER_` prefix for all context variables: + +```bash +#!/usr/bin/env bash +# +# Install Package +# ------------------------------------------------------------------------------- +# Required Environment Variables: +# DEPLOYER_DISTRO - Distribution (debian|redhat|amazon) +# DEPLOYER_PERMS - Permission level (root|sudo) + +set -o pipefail +export DEBIAN_FRONTEND=noninteractive + +# Validation +if [[ -z $DEPLOYER_DISTRO ]]; then + echo "Error: DEPLOYER_DISTRO environment variable is required" + exit 1 +fi +``` + +### Idempotency + +ALL playbooks MUST be idempotent - safe to run multiple times without side effects. + +Check before acting - don't fail if resource exists: + +```bash +# βœ… CORRECT - Idempotent +if ! command -v caddy >/dev/null 2>&1; then + run_cmd apt-get install -y -q caddy +fi + +if [[ ! -d /var/www/app ]]; then + run_cmd mkdir -p /var/www/app +fi + +# ❌ WRONG - Not idempotent (fails on second run) +run_cmd useradd deployer + +# ❌ WRONG - Not idempotent (duplicates each run) +echo "export PATH=\$PATH:/usr/local/bin" >> ~/.bashrc +``` + +Enables recovery, resumption, drift correction. + +### Error Handling + +Fail fast with clear messages: + +```bash +set -o pipefail # Fail on pipe errors + +if [[ -z $DEPLOYER_DISTRO ]]; then + echo "Error: DEPLOYER_DISTRO environment variable is required" + exit 1 +fi +``` + +### Helper Functions + +```bash +# Execute with appropriate permissions +run_cmd() { + if [[ $DEPLOYER_PERMS == 'root' ]]; then + "$@" + else + sudo "$@" + fi +} + +# Usage +run_cmd apt-get install -y -q package-name +``` + +### Output + +- Use YAML for structured data output +- Echo progress messages for logs +- Use `βœ“` for success, `βœ—` for failure +- Keep output clean and scannable + +### Complete Example + +```bash +#!/usr/bin/env bash +set -o pipefail +export DEBIAN_FRONTEND=noninteractive + +# Validation +[[ -z $DEPLOYER_DISTRO ]] && echo "Error: DEPLOYER_DISTRO required" && exit 1 + +# Helpers +run_cmd() { [[ $DEPLOYER_PERMS == 'root' ]] && "$@" || sudo "$@"; } + +# Task 1: Install package (idempotent) +if ! command -v nginx >/dev/null 2>&1; then + run_cmd apt-get install -y -q nginx + echo "βœ“ Nginx installed" +else + echo "βœ“ Nginx already installed" +fi + +# Task 2: Create directory (idempotent) +if [[ ! -d /var/www/app ]]; then + run_cmd mkdir -p /var/www/app + echo "βœ“ Directory created" +else + echo "βœ“ Directory already exists" +fi + +# Task 3: Configure service (idempotent) +if ! systemctl is-enabled --quiet nginx; then + run_cmd systemctl enable --quiet nginx + echo "βœ“ Nginx enabled" +else + echo "βœ“ Nginx already enabled" +fi + +echo "βœ“ Setup complete" +``` + +See: server-info.sh diff --git a/.cursor/rules/rules.mdc b/.cursor/rules/rules.mdc new file mode 100644 index 00000000..40d83021 --- /dev/null +++ b/.cursor/rules/rules.mdc @@ -0,0 +1,165 @@ +--- +alwaysApply: false +--- + +# Rules for Writing Rules + +Guidelines for creating and maintaining rule files optimized for AI agents with limited token windows. + +## Token Efficiency Principles + +- Use imperative mood, not conversational prose +- One example per pattern maximum +- Remove "Benefits", "Why This Matters", "Key Benefits" sections +- Use inline comments over separate explanations +- Use bullets over paragraphs +- Single "All rules MANDATORY" statement per file +- No repetitive CRITICAL/IMMUTABLE warnings +- No emoji in headers (wastes tokens, no AI value) + +## Structure Standards + +**File Header:** + +```yaml +--- +alwaysApply: true +--- +## [Section Name] + +All rules MANDATORY unless marked optional. +``` + +**Section Organization:** + +- Clear, scannable headers +- Related rules grouped together +- Alphabetical ordering when no logical grouping exists +- No more than 3 heading levels + +## Example Guidelines + +**Pattern: Show correct first, wrong only when non-obvious** + +```php +// βœ… CORRECT +$result = $container->build(Service::class); + +// ❌ WRONG - manual instantiation breaks DI +$result = new Service(new Dependency()); +``` + +**Rules:** + +- Keep examples under 10 lines +- Use `// βœ… CORRECT` and `// ❌ WRONG` markers consistently +- Prefer inline comments to prose explanations +- Remove examples for well-known patterns (AAA, SOLID, etc.) +- Don't explain framework features (Laravel Prompts, Pest, Symfony) + +## Cross-File Coordination + +**Avoid Duplication:** + +- Single source of truth per concept +- If rule appears in multiple contexts, pick primary location +- Cross-reference by filename only: "See 03-commands.mdc" +- No line number references (brittle) + +**Valid Cross-References:** + +```markdown +See [ServerValidationTrait.php](mdc:app/Traits/ServerValidationTrait.php) +Covered in 03-commands.mdc +``` + +**Verify Links:** + +- All `mdc://` references must point to existing files +- Remove references to deleted files immediately + +## Maintenance Checklist + +Before committing rule changes: + +1. Remove outdated file references +2. Check for duplication with other rule files +3. Verify no contradictions introduced +4. Test that code examples compile/run +5. Run token count comparison (target: 35-65% of original verbosity) +6. Confirm critical rules still emphasized (but not repetitively) + +## Anti-Patterns + +**Avoid:** + +- Copying third-party documentation verbatim (summarize key points only) +- Multiple examples showing identical patterns +- Teaching language/framework fundamentals +- Explaining obvious concepts +- Tables where bullet lists suffice +- Conversational explanations of patterns shown in code + +**Example - Too Verbose:** + +```markdown +**How It Works:** + +1. Container analyzes constructor via reflection +2. Recursively builds dependencies +3. Caches reflection data +4. Handles errors gracefully + +**Key Benefits:** + +- Zero configuration required +- Type-safe with generics +- Easy testing with mocks +``` + +**Example - Optimized:** + +```markdown +Use `$container->build(Class::class)` for all object creation. +Exceptions: DTOs, value objects, pure data structures. +``` + +## Rule Density Guidelines + +**Target ratios (lines of rules per file):** + +- Meta/philosophy: 30-50 lines +- Architecture/patterns: 80-120 lines +- Testing: 80-120 lines +- Console/CLI: 180-220 lines +- Language style (Bash/PHP): 60-100 lines +- Project-specific (Playbooks): 100-140 lines + +**Token budget awareness:** + +- AI agents may have 8K-32K context windows +- Rules should consume <20% of available tokens +- Leave 80% for code, history, and responses +- Total rule corpus target: <3000 tokens (~600-800 lines) + +## Writing Style + +**Prefer:** + +```markdown +Commands handle user I/O. Services contain business logic. No circular dependencies. +``` + +**Over:** + +```markdown +Commands are responsible for handling all user interaction including input and output operations, while Services provide the core business logic functionality. It's important to note that circular dependencies between these layers are not allowed and should be avoided at all costs. +``` + +**Emphasis Hierarchy:** + +1. Code examples (most efficient) +2. Imperative bullets +3. Short declarative sentences +4. Tables (only for reference data) +5. Prose explanations (last resort) From 7faf70520b0a862adba6616a800e19de58ea024b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucian=20V=C4=83c=C4=83roiu?= Date: Fri, 24 Oct 2025 22:57:11 +0300 Subject: [PATCH 2/2] fixup --- .cursor/rules/00-main.mdc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.cursor/rules/00-main.mdc b/.cursor/rules/00-main.mdc index 5fad314e..aea25374 100644 --- a/.cursor/rules/00-main.mdc +++ b/.cursor/rules/00-main.mdc @@ -51,4 +51,4 @@ Build Deployer PHP: Composer package and CLI tool simplifying server provisionin Don't run or create or update tests UNLESS explicitly instructed to do so. -Test are something we need focus on separately from building features. +Test are something we need to focus on separately from building features.