diff --git a/.claude/skills/echo-development/SKILL.md b/.claude/skills/echo-development/SKILL.md new file mode 100644 index 0000000..9b8264b --- /dev/null +++ b/.claude/skills/echo-development/SKILL.md @@ -0,0 +1,214 @@ +--- +name: echo-development +description: "Develops real-time broadcasting with Laravel Echo. Activates when setting up broadcasting (Reverb, Pusher, Ably); creating ShouldBroadcast events; defining broadcast channels (public, private, presence, encrypted); authorizing channels; configuring Echo; listening for events; implementing client events (whisper); setting up model broadcasting; broadcasting notifications; or when the user mentions broadcasting, Echo, WebSockets, real-time events, Reverb, or presence channels." +license: MIT +metadata: + author: laravel +--- + +# Laravel Broadcasting & Echo + +## When to Apply + +Activate this skill when: + +- Installing or configuring Laravel broadcasting (Reverb, Pusher, Ably) +- Creating events that implement `ShouldBroadcast` +- Defining broadcast channels and authorization +- Setting up Laravel Echo on the client side +- Listening for broadcast events, notifications, or model events +- Implementing client-to-client events (whisper) +- Working with presence channels for user awareness + +## Documentation + +Use `search-docs` for detailed broadcasting patterns and documentation. + +## Basic Usage + +### Installing Broadcasting + +```bash +php artisan install:broadcasting +``` + +Use flags for specific drivers: `--reverb`, `--pusher`, `--ably`. This creates `config/broadcasting.php` and `routes/channels.php`. + +### Creating a Broadcast Event + +```bash +php artisan make:event OrderShipped +``` + + +```php +namespace App\Events; + +use App\Models\Order; +use Illuminate\Broadcasting\InteractsWithSockets; +use Illuminate\Broadcasting\PrivateChannel; +use Illuminate\Contracts\Broadcasting\ShouldBroadcast; +use Illuminate\Queue\SerializesModels; + +class OrderShipped implements ShouldBroadcast +{ + use InteractsWithSockets, SerializesModels; + + public function __construct(public Order $order) {} + + public function broadcastOn(): array + { + return [new PrivateChannel('orders.'.$this->order->id)]; + } +} +``` + +Dispatch the event: + + +```php +use App\Events\OrderShipped; + +OrderShipped::dispatch($order); +``` + +### Authorizing Channels + +Define authorization in `routes/channels.php`: + + +```php +use App\Models\Order; +use App\Models\User; + +Broadcast::channel('orders.{orderId}', function (User $user, int $orderId) { + return $user->id === Order::findOrNew($orderId)->user_id; +}); +``` + +Create a channel class for complex authorization: + +```bash +php artisan make:channel OrderChannel +``` + +List all registered channels: + +```bash +php artisan channel:list +``` + +### Client-Side Setup + +Install Echo and Pusher JS: + +```bash +npm install --save-dev laravel-echo pusher-js +``` + + +```javascript +import Echo from 'laravel-echo'; +import Pusher from 'pusher-js'; +window.Pusher = Pusher; + +window.Echo = new Echo({ + broadcaster: 'reverb', + key: import.meta.env.VITE_REVERB_APP_KEY, + wsHost: import.meta.env.VITE_REVERB_HOST, + wsPort: import.meta.env.VITE_REVERB_PORT ?? 80, + wssPort: import.meta.env.VITE_REVERB_PORT ?? 443, + forceTLS: (import.meta.env.VITE_REVERB_SCHEME ?? 'https') === 'https', + enabledTransports: ['ws', 'wss'], +}); +``` + +### Listening for Events + + +```javascript +Echo.private(`orders.${orderId}`) + .listen('OrderShipmentStatusUpdated', (e) => { + console.log(e.order); + }); +``` + +### Running Required Processes + +```bash +php artisan queue:work # Required for ShouldBroadcast events + +php artisan reverb:start # Required for Reverb driver + +``` + +## What's Possible + +Use `search-docs` to find detailed code examples and configuration for each of these: + +### Channel Types + +- Public (`new Channel`) — no auth, anyone can subscribe. Use for app-wide announcements, public feeds, or status pages. +- Private (`new PrivateChannel`) — requires authorization. Use for user-specific data like orders, messages, or account updates. +- Presence (`new PresenceChannel`) — authorized + tracks who's online. Use for chat rooms, collaborative editing, "who's viewing this" features, or typing indicators. +- EncryptedPrivate — end-to-end encryption, Pusher/Reverb only. Use when payload must be hidden from the broadcast server (e.g., sensitive financial data or private messages). +- Drivers: `reverb` (self-hosted WebSocket server), `pusher` (managed service), `ably` (managed service), `log` (writes to Laravel log, use for debugging), `null` (no-op, use for testing) + +### Event Customization + +- `broadcastAs()` — custom event name (client must use dot prefix: `.listen('.custom.name')`). Use when you want stable API names decoupled from PHP class names, or shorter event names for the frontend. +- `broadcastWith()` — control exact payload. Use to avoid leaking sensitive model attributes, slim down large payloads, or add computed data not on the model. +- `broadcastWhen()` — conditional broadcasting. Use to skip broadcasting when changes are trivial (e.g., only broadcast order updates above a threshold, or skip unchanged fields). +- `broadcastQueue()` / `$queue` — route to specific queue. Use to isolate real-time broadcasts from slow background jobs so they're processed faster. +- `$connection` — set queue connection per event. Use when broadcasts should go through a faster queue backend like Redis while other jobs use the database driver. + +### Broadcasting Interfaces + +- `ShouldBroadcast` — queue the broadcast (default). Use for most events to avoid blocking the HTTP response. +- `ShouldBroadcastNow` — broadcast synchronously, skip queue. Use during development or for time-critical events where queue latency is unacceptable. +- `ShouldDispatchAfterCommit` — wait for DB transaction commit. Use when the event references newly created records that listeners need to query (prevents race conditions). +- `ShouldRescue` — auto-catch broadcast exceptions. Use to prevent broadcast failures (e.g., WebSocket server down) from disrupting the user's HTTP request. +- `InteractsWithSockets` — required for `toOthers()`. Use on any event where you want to exclude the sender (optimistic UI updates). +- `InteractsWithBroadcasting` — override driver per event via `broadcastVia()`. Use in multi-driver setups (e.g., some events via Reverb, others via Pusher). + +### Broadcasting Helpers + +- `broadcast(new Event)->toOthers()` — exclude current user's socket. Use when the client already updates optimistically from the API response to avoid duplicate updates. +- `broadcast(new Event)->via('pusher')` — override connection. Use to route specific events through a different broadcast driver than the default. +- `Broadcast::on()`, `Broadcast::private()`, `Broadcast::presence()` — anonymous broadcasting without event classes. Chain `.as('name')->with($data)->send()` or `.sendNow()`. Use for simple one-off broadcasts where creating a full event class is overkill (e.g., quick status updates, simple notifications). + +### Channel Authorization + +- Closure-based in `routes/channels.php` — use for simple authorization logic (e.g., checking ownership). +- Model binding: `Broadcast::channel('orders.{order}', fn (User $user, Order $order) => ...)` — use when authorization depends on the model instance (auto-resolves from route parameter). +- Channel classes via `php artisan make:channel` — use for complex authorization logic that benefits from dependency injection or reusable logic across channels. +- Multiple guards: `['guards' => ['web', 'admin']]` — use when the channel should be accessible by users authenticated via different guards (e.g., both regular users and admins). + +### Model Broadcasting + +- `BroadcastsEvents` trait auto-broadcasts created/updated/deleted/trashed/restored. Use to automatically keep clients in sync with Eloquent model changes without writing individual events. +- Channel convention: `App.Models.Post.{id}` — clients subscribe to model-specific channels. +- `broadcastAs($event)` and `broadcastWith($event)` for per-action customization. Use to send different payloads for create vs update, or suppress certain event types. +- `newBroadcastableEvent($event)` for event instance customization (e.g., `->dontBroadcastToCurrentUser()`). Use when you need to modify the underlying event object before it's dispatched. + +### Client-Side Features + +- Client events: `whisper()` / `listenForWhisper()` — peer-to-peer without server roundtrip (private/presence channels only). Use for typing indicators, cursor positions, or any ephemeral state that doesn't need server persistence. +- Presence channels: `Echo.join()` with `here()`, `joining()`, `leaving()`, `error()` callbacks. Use for showing online users, "X is viewing this document" features, or live participant counts. +- Notification broadcasting: `.notification()` on user's private channel. Use to show real-time notifications (toast, badge counts) pushed from Laravel's notification system. +- Connection management: `Echo.connectionStatus()`, `Echo.leaveAllChannels()`, `Echo.disconnect()`. Use to show connection indicators, clean up on logout, or handle offline/reconnect scenarios. +- Custom namespace: `new Echo({ namespace: 'App.Other.Namespace' })`. Use when your events live outside the default `App\Events` namespace. + +## Common Pitfalls + +- Queue worker must be running for `ShouldBroadcast` events. Use `ShouldBroadcastNow` during development. +- `BROADCAST_CONNECTION` not `BROADCAST_DRIVER`: Laravel 11+ renamed this env key. +- `toOthers()` requires `InteractsWithSockets` trait AND `X-Socket-ID` header. Echo auto-adds this to global Axios. For `fetch`, manually send `Echo.socketId()`. +- CORS: When frontend/backend are on different origins, add `broadcasting/auth` to `config/cors.php` paths and set `supports_credentials` to `true`. +- Missing `VITE_` prefix: Client-side env vars must start with `VITE_`. +- `channels.php` not loaded: Verify it's included in `withRouting()` in `bootstrap/app.php`. +- Reverb is long-running: Code changes require `php artisan reverb:restart`. +- Presence channel auth must return an array of user data (`['id' => $user->id, 'name' => $user->name]`), not `true`. Returning `true` silently fails. +- Dot prefix rule: When using `broadcastAs()`, client must prefix with `.` (e.g., `.listen('.custom.name')`). Without the dot, Echo looks for `App\Events\custom.name` which silently fails. +- Reverb host separation: `REVERB_SERVER_HOST`/`REVERB_SERVER_PORT` (internal bind) vs `REVERB_HOST`/`REVERB_PORT` (public address) vs `VITE_REVERB_HOST`/`VITE_REVERB_PORT` (client JS). +- Sanctum SPA auth: Ensure `/broadcasting/auth` uses `auth:sanctum` middleware and CSRF tokens are sent with `withCredentials: true`. diff --git a/.claude/skills/fluxui-development/SKILL.md b/.claude/skills/fluxui-development/SKILL.md new file mode 100644 index 0000000..d4fb5a0 --- /dev/null +++ b/.claude/skills/fluxui-development/SKILL.md @@ -0,0 +1,81 @@ +--- +name: fluxui-development +description: "Use this skill for Flux UI development in Livewire applications only. Trigger when working with components, building or customizing Livewire component UIs, creating forms, modals, tables, or other interactive elements. Covers: flux: components (buttons, inputs, modals, forms, tables, date-pickers, kanban, badges, tooltips, etc.), component composition, Tailwind CSS styling, Heroicons/Lucide icon integration, validation patterns, responsive design, and theming. Do not use for non-Livewire frameworks or non-component styling." +license: MIT +metadata: + author: laravel +--- + +# Flux UI Development + +## Documentation + +Use `search-docs` for detailed Flux UI patterns and documentation. + +## Basic Usage + +This project uses the free edition of Flux UI, which includes all free components and variants but not Pro components. + +Flux UI is a component library for Livewire built with Tailwind CSS. It provides components that are easy to use and customize. + +Use Flux UI components when available. Fall back to standard Blade components when no Flux component exists for your needs. + + +```blade +Click me +``` + +## Available Components (Free Edition) + +Available: avatar, badge, brand, breadcrumbs, button, callout, card, checkbox, dropdown, field, heading, icon, input, modal, navbar, otp-input, pagination, profile, progress, radio, select, separator, skeleton, switch, table, text, textarea, toast, tooltip + +## Icons + +Flux includes [Heroicons](https://heroicons.com/) as its default icon set. Search for exact icon names on the Heroicons site - do not guess or invent icon names. + + +```blade +Export +``` + +For icons not available in Heroicons, use [Lucide](https://lucide.dev/). Import the icons you need with the Artisan command: + +```bash +php artisan flux:icon crown grip-vertical github +``` + +## Common Patterns + +### Form Fields + + +```blade + + Email + + + +``` + +### Modals + + +```blade + + Title +

Content

+
+``` + +## Verification + +1. Check component renders correctly +2. Test interactive states +3. Verify mobile responsiveness + +## Common Pitfalls + +- Trying to use Pro-only components in the free edition +- Not checking if a Flux component exists before creating custom implementations +- Forgetting to use the `search-docs` tool for component-specific documentation +- Not following existing project patterns for Flux usage diff --git a/.claude/skills/fortify-development/SKILL.md b/.claude/skills/fortify-development/SKILL.md new file mode 100644 index 0000000..2c4e84f --- /dev/null +++ b/.claude/skills/fortify-development/SKILL.md @@ -0,0 +1,151 @@ +--- +name: fortify-development +description: 'ACTIVATE when the user works on authentication in Laravel. This includes login, registration, password reset, email verification, two-factor authentication (2FA/TOTP/QR codes/recovery codes), passkeys, profile updates, password confirmation, or any auth-related routes and controllers. Activate when the user mentions Fortify, auth, authentication, login, register, signup, forgot password, verify email, 2FA, passkeys, WebAuthn, or references app/Actions/Fortify/, CreateNewUser, UpdateUserProfileInformation, FortifyServiceProvider, config/fortify.php, or auth guards. Fortify is the frontend-agnostic authentication backend for Laravel that registers all auth routes and controllers. Also activate when building SPA or headless authentication, customizing login redirects, overriding response contracts like LoginResponse, or configuring login throttling. Do NOT activate for Laravel Passport (OAuth2 API tokens), Socialite (OAuth social login), or non-auth Laravel features.' +license: MIT +metadata: + author: laravel +--- + +# Laravel Fortify Development + +Fortify is a headless authentication backend that provides authentication routes and controllers for Laravel applications. + +## Documentation + +Use `search-docs` for detailed Laravel Fortify patterns and documentation. + +## Usage + +- **Routes**: Use `list-routes` with `only_vendor: true` and `action: "Fortify"` to see all registered endpoints +- **Actions**: Check `app/Actions/Fortify/` for customizable business logic (user creation, password validation, etc.) +- **Config**: See `config/fortify.php` for all options including features, guards, rate limiters, and username field +- **Contracts**: Look in `Laravel\Fortify\Contracts\` for overridable response classes (`LoginResponse`, `LogoutResponse`, etc.) +- **Views**: All view callbacks are set in `FortifyServiceProvider::boot()` using `Fortify::loginView()`, `Fortify::registerView()`, etc. + +## Available Features + +Enable in `config/fortify.php` features array: + +- `Features::registration()` - User registration +- `Features::resetPasswords()` - Password reset via email +- `Features::emailVerification()` - Requires User to implement `MustVerifyEmail` +- `Features::updateProfileInformation()` - Profile updates +- `Features::updatePasswords()` - Password changes +- `Features::twoFactorAuthentication()` - 2FA with QR codes and recovery codes +- `Features::passkeys()` - Passwordless authentication with WebAuthn passkeys + +> Use `search-docs` for feature configuration options and customization patterns. + +## Setup Workflows + +### Two-Factor Authentication Setup + +``` +- [ ] Add TwoFactorAuthenticatable trait to User model +- [ ] Enable feature in config/fortify.php +- [ ] If the `*_add_two_factor_columns_to_users_table.php` migration is missing, publish via `php artisan vendor:publish --tag=fortify-migrations` and migrate +- [ ] Set up view callbacks in FortifyServiceProvider +- [ ] Create 2FA management UI +- [ ] Test QR code and recovery codes +``` + +> Use `search-docs` for TOTP implementation and recovery code handling patterns. + +### Passkeys Setup + +``` +- [ ] Add PasskeyAuthenticatable trait to User model and implement PasskeyUser +- [ ] Enable passkeys feature in config/fortify.php +- [ ] If the passkeys table migration is missing, publish via `php artisan vendor:publish --tag=fortify-migrations` and migrate +- [ ] Configure passkeys relying_party_id, allowed_origins, user_handle_secret, and timeout if defaults are not suitable +- [ ] Build UI with @laravel/passkeys for registration, login, confirmation, and deletion +``` + +> Use `search-docs` for passkey configuration options. For `@laravel/passkeys` frontend usage, refer to the package's README on npm. + +### Email Verification Setup + +``` +- [ ] Enable emailVerification feature in config +- [ ] Implement MustVerifyEmail interface on User model +- [ ] Set up verifyEmailView callback +- [ ] Add verified middleware to protected routes +- [ ] Test verification email flow +``` + +> Use `search-docs` for MustVerifyEmail implementation patterns. + +### Password Reset Setup + +``` +- [ ] Enable resetPasswords feature in config +- [ ] Set up requestPasswordResetLinkView callback +- [ ] Set up resetPasswordView callback +- [ ] Define password.reset named route (if views disabled) +- [ ] Test reset email and link flow +``` + +> Use `search-docs` for custom password reset flow patterns. + +### SPA Authentication Setup + +``` +- [ ] Set 'views' => false in config/fortify.php +- [ ] Install and configure Laravel Sanctum for session-based SPA authentication +- [ ] Use the 'web' guard in config/fortify.php (required for session-based authentication) +- [ ] Set up CSRF token handling +- [ ] Test XHR authentication flows +``` + +> Use `search-docs` for integration and SPA authentication patterns. + +#### Two-Factor Authentication in SPA Mode + +When `views` is set to `false`, Fortify returns JSON responses instead of redirects. + +If a user attempts to log in and two-factor authentication is enabled, the login request will return a JSON response indicating that a two-factor challenge is required: + +```json +{ + "two_factor": true +} +``` + +## Best Practices + +### Custom Authentication Logic + +Override authentication behavior using `Fortify::authenticateUsing()` for custom user retrieval or `Fortify::authenticateThrough()` to customize the authentication pipeline. Override response contracts in `AppServiceProvider` for custom redirects. + +### Registration Customization + +Modify `app/Actions/Fortify/CreateNewUser.php` to customize user creation logic, validation rules, and additional fields. + +### Rate Limiting + +Configure via `fortify.limiters.login` in config. Default configuration throttles by username + IP combination. + +## Key Endpoints + +| Feature | Method | Endpoint | +|------------------------|----------|---------------------------------------------| +| Login | POST | `/login` | +| Logout | POST | `/logout` | +| Register | POST | `/register` | +| Password Reset Request | POST | `/forgot-password` | +| Password Reset | POST | `/reset-password` | +| Email Verify Notice | GET | `/email/verify` | +| Resend Verification | POST | `/email/verification-notification` | +| Password Confirm | POST | `/user/confirm-password` | +| Enable 2FA | POST | `/user/two-factor-authentication` | +| Confirm 2FA | POST | `/user/confirmed-two-factor-authentication` | +| 2FA Challenge | POST | `/two-factor-challenge` | +| Get QR Code | GET | `/user/two-factor-qr-code` | +| Recovery Codes | GET/POST | `/user/two-factor-recovery-codes` | +| Passkey Login Options | GET | `/passkeys/login/options` | +| Passkey Login | POST | `/passkeys/login` | +| Passkey Confirm Options| GET | `/passkeys/confirm/options` | +| Passkey Confirm | POST | `/passkeys/confirm` | +| Passkey Options | GET | `/user/passkeys/options` | +| Register Passkey | POST | `/user/passkeys` | +| Delete Passkey | DELETE | `/user/passkeys/{passkey}` | diff --git a/.claude/skills/infer-conventions/SKILL.md b/.claude/skills/infer-conventions/SKILL.md index e7fcafc..11a9327 100644 --- a/.claude/skills/infer-conventions/SKILL.md +++ b/.claude/skills/infer-conventions/SKILL.md @@ -30,7 +30,7 @@ Fan out when you can. The sweep is embarrassingly parallel. If your environment Read `composer.json` (installed packages tell you which checklist groups apply), the `pint.json` / PHPStan / Rector config, `.ai/rules/index.md` if present, and most important, map the `app/` tree. List every directory under `app/` (and any `Modules/`, `src/`, `packages/`, or domain root). Every folder beyond Laravel's default skeleton (`Http`, `Models`, `Providers`, `Console`, `Exceptions`) is a structural pattern the app committed to and a high-value rule waiting to be written: `Actions`, `Services`, `Data` or DTOs, `Queries`, `Repositories`, `ViewModels`, `Pipelines`, `Support`, `Enums`, `Contracts`, `Observers`, or `Domain` and module roots. Note each one. You will confirm how it is used in Step 2. -This app has no Livewire/Inertia/Flux packages installed. Treat the frontend group as likely API-only: confirm from `resources/views` before spending time there, and skip the Livewire/Inertia/Flux dimensions. +This app ships a frontend stack, so the frontend checklist group applies. Sweep it. Done when: you have the applicable checklist groups, the dimensions already recorded in `.ai/rules`, and a list of every non-default `app/` directory mapped to the pattern it represents. diff --git a/.claude/skills/infer-conventions/references/checklist.md b/.claude/skills/infer-conventions/references/checklist.md index 80cef34..2b45cc2 100644 --- a/.claude/skills/infer-conventions/references/checklist.md +++ b/.claude/skills/infer-conventions/references/checklist.md @@ -77,12 +77,16 @@ Each item gives the fork, then a hint (a grep or dir to spot which side the app ## F. Frontend & views -No Livewire/Inertia/Flux package is installed. This app may be API-only. Confirm from `resources/views` before sweeping, and treat the Livewire/Flux dimensions as not applicable. +This app ships a frontend stack, so the items below apply. 28. Frontend stack: Blade+Livewire vs Inertia (Vue/React/Svelte) vs Blade-only / API + separate SPA. - Hint: `composer.json` + `package.json`; `ls resources/js/pages`, `resources/views`. 29. Blade composition: class `` components vs anonymous components (`@props`) vs `@include` partials. - Hint: `ls app/View/Components`; grep ` **Always follow the project's existing conventions first.** Before creating any component, inspect the project's existing Livewire components to determine the established format (SFC, MFC, or class-based) and directory structure. Check `app/Livewire/`, `resources/views/components/`, and `resources/views/livewire/` for existing components. If the project already uses a consistent format, **use that same format** — even if it differs from the Livewire v4 defaults below. Only fall back to the v4 defaults (SFC in `resources/views/components/`) when no existing convention is established. + +Also check `config/livewire.php` for `make_command.type`, `make_command.emoji`, `component_locations`, and `component_namespaces` overrides, which change the default format and where files are stored. + +### Component Format Reference + +| Format | Flag | Class Path | View Path | +|--------|------|------------|-----------| +| Single-file (SFC) | default | — | `resources/views/components/⚡create-post.blade.php` (PHP + Blade in one file) | +| Full Page SFC | `pages::name` | — | `resources/views/pages/⚡create-post.blade.php` | +| Multi-file (MFC) | `--mfc` | `resources/views/components/⚡create-post/create-post.php` | `resources/views/components/⚡create-post/create-post.blade.php` | +| Class-based | `--class` | `app/Livewire/CreatePost.php` | `resources/views/livewire/create-post.blade.php` | +| View-based | default (Blade-only) | — | `resources/views/components/⚡create-post.blade.php` (Blade-only with functional state) | + +> **Important:** The ⚡ prefix shown above is the **default** behavior in Livewire v4 — it is **configurable**. Check `config/livewire.php` for the `make_command.emoji` setting. When `true` (default), always include the ⚡ prefix in filenames you create. When `false`, omit the ⚡ prefix from all paths above. + +Namespaced components map to subdirectories: `make:livewire Posts/CreatePost` creates `resources/views/components/posts/⚡create-post.blade.php` (single-file by default). Use `make:livewire Posts/CreatePost --mfc` for multi-file output at `resources/views/components/posts/⚡create-post/create-post.php` and `resources/views/components/posts/⚡create-post/create-post.blade.php`. + +### Single-File Component Example + + +```php +count++; + } +}; +?> + +
+ +
+``` + +## Livewire 4 Specifics + +### Key Changes From Livewire 3 + +These things changed in Livewire 4, but may not have been updated in this application. Verify this application's setup to ensure you follow existing conventions. + +- Use `Route::livewire()` for full-page components (e.g., `Route::livewire('/posts/create', CreatePost::class)`); config keys renamed: `layout` → `component_layout`, `lazy_placeholder` → `component_placeholder`. +- `wire:model` now ignores child events by default (use `wire:model.deep` for old behavior); `wire:scroll` renamed to `wire:navigate:scroll`. +- Component tags must be properly closed; `wire:transition` now uses View Transitions API (modifiers removed). +- JavaScript: `$wire.$js('name', fn)` → `$wire.$js.name = fn`; `commit`/`request` hooks → `interceptMessage()`/`interceptRequest()`. + +### New Features + +- Component formats: single-file (SFC), multi-file (MFC), view-based components. +- Islands (`@island`) for isolated updates; async actions (`wire:click.async`, `#[Async]`) for parallel execution. +- Deferred/bundled loading: `defer`, `lazy.bundle` for optimized component loading. + +| Feature | Usage | Purpose | +|---------|-------|---------| +| Islands | `@island(name: 'stats')` | Isolated update regions | +| Async | `wire:click.async` or `#[Async]` | Non-blocking actions | +| Deferred | `defer` attribute | Load after page render | +| Bundled | `lazy.bundle` | Load multiple together | + +### New Directives + +- `wire:sort`, `wire:intersect`, `wire:ref`, `.renderless`, `.preserve-scroll` are available for use. +- `data-loading` attribute automatically added to elements triggering network requests. + +| Directive | Purpose | +|-----------|---------| +| `wire:sort` | Drag-and-drop sorting | +| `wire:intersect` | Viewport intersection detection | +| `wire:ref` | Element references for JS | +| `.renderless` | Component without rendering | +| `.preserve-scroll` | Preserve scroll position | + +## Best Practices + +- Always use `wire:key` in loops +- Use `wire:loading` for loading states +- Use `wire:model.live` for instant updates (default is debounced) +- Validate and authorize in actions (treat like HTTP requests) + +## Configuration + +- `smart_wire_keys` defaults to `true`; new configs: `component_locations`, `component_namespaces`, `make_command`, `csp_safe`. + +## Alpine & JavaScript + +- `wire:transition` uses browser View Transitions API; `$errors` and `$intercept` magic properties available. +- Non-blocking `wire:poll` and parallel `wire:model.live` updates improve performance. + +For interceptors and hooks, see [reference/javascript-hooks.md](reference/javascript-hooks.md). + +## Testing + + +```php +Livewire::test(Counter::class) + ->assertSet('count', 0) + ->call('increment') + ->assertSet('count', 1); +``` + +## Verification + +1. Browser console: Check for JS errors +2. Network tab: Verify Livewire requests return 200 +3. Ensure `wire:key` on all `@foreach` loops + +## Common Pitfalls + +- Missing `wire:key` in loops → unexpected re-rendering +- Expecting `wire:model` real-time → use `wire:model.live` +- Unclosed component tags → syntax errors in v4 +- Using deprecated config keys or JS hooks +- Including Alpine.js separately (already bundled in Livewire 4) diff --git a/.claude/skills/livewire-development/reference/javascript-hooks.md b/.claude/skills/livewire-development/reference/javascript-hooks.md new file mode 100644 index 0000000..660d66b --- /dev/null +++ b/.claude/skills/livewire-development/reference/javascript-hooks.md @@ -0,0 +1,39 @@ +# Livewire 4 JavaScript Integration + +## Interceptor System (v4) + +### Intercept Messages + +```js +Livewire.interceptMessage(({ component, message, onFinish, onSuccess, onError }) => { + onFinish(() => { /* After response, before processing */ }); + onSuccess(({ payload }) => { /* payload.snapshot, payload.effects */ }); + onError(() => { /* Server errors */ }); +}); +``` + +### Intercept Requests + +```js +Livewire.interceptRequest(({ request, onResponse, onSuccess, onError, onFailure }) => { + onResponse(({ response }) => { /* When received */ }); + onSuccess(({ response, responseJson }) => { /* Success */ }); + onError(({ response, responseBody, preventDefault }) => { /* 4xx/5xx */ }); + onFailure(({ error }) => { /* Network failures */ }); +}); +``` + +### Component-Scoped Interceptors + +```blade + +``` + +## Magic Properties + +- `$errors` - Access validation errors from JavaScript +- `$intercept` - Component-scoped interceptors diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..c6f1b63 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,177 @@ + +=== foundation rules === + +# Laravel Boost Guidelines + +The Laravel Boost guidelines are specifically curated by Laravel maintainers for this application. These guidelines should be followed closely to ensure the best experience when building Laravel applications. + +## Foundational Context + +This application is a Laravel application running on PHP 8.5. You are an expert with the Laravel ecosystem. Always use the APIs that match the installed major version of each package — do not assume a version. + +Before relying on a package's API, confirm its installed version: +- PHP packages: run `composer show --direct` to list direct dependencies with versions, or `composer show ` for a single package. +- JS packages: check `package.json` for the installed versions. + +## Skills Activation + +This project has domain-specific skills available in `**/skills/**`. You MUST activate the relevant skill whenever you work in that domain—don't wait until you're stuck. + +## Conventions + +- You must follow all existing code conventions used in this application. When creating or editing a file, check sibling files for the correct structure, approach, and naming. +- Use descriptive names for variables and methods. For example, `isRegisteredForDiscounts`, not `discount()`. +- Check for existing components to reuse before writing a new one. + +## Verification Scripts + +- Do not create verification scripts or tinker when tests cover that functionality and prove they work. Unit and feature tests are more important. + +## Application Structure & Architecture + +- Stick to existing directory structure; don't create new base folders without approval. +- Do not change the application's dependencies without approval. + +## Frontend Bundling + +- If the user doesn't see a frontend change reflected in the UI, it could mean they need to run `npm run build`, `npm run dev`, or `composer run dev`. Ask them. + +## Documentation Files + +- You must only create documentation files if explicitly requested by the user. + +## Replies + +- Be concise in your explanations - focus on what's important rather than explaining obvious details. + +=== boost rules === + +# Laravel Boost + +## Tools + +- Laravel Boost is an MCP server with tools designed specifically for this application. Prefer Boost tools over manual alternatives like shell commands or file reads. +- Use `database-query` to run read-only queries against the database instead of writing raw SQL in tinker. +- Use `database-schema` to inspect table structure before writing migrations or models. +- Use `get-absolute-url` to resolve the correct scheme, domain, and port for project URLs. Always use this before sharing a URL with the user. +- Use `browser-logs` to read browser logs, errors, and exceptions. Only recent logs are useful, ignore old entries. + +## Searching Documentation (IMPORTANT) + +- Always use `search-docs` before making code changes. Do not skip this step. It returns version-specific docs based on installed packages automatically. +- Pass a `packages` array to scope results when you know which packages are relevant. +- Use multiple broad, topic-based queries: `['rate limiting', 'routing rate limiting', 'routing']`. Expect the most relevant results first. +- Do not add package names to queries because package info is already shared. Use `test resource table`, not `filament 4 test resource table`. + +### Search Syntax + +1. Use words for auto-stemmed AND logic: `rate limit` matches both "rate" AND "limit". +2. Use `"quoted phrases"` for exact position matching: `"infinite scroll"` requires adjacent words in order. +3. Combine words and phrases for mixed queries: `middleware "rate limit"`. +4. Use multiple queries for OR logic: `queries=["authentication", "middleware"]`. + +## Project Rules + +- This project contains committed, area-grouped rules in `.ai/rules` when that directory exists (settled decisions, non-obvious traps, standing constraints). Framework and package guidelines that only apply to specific paths (testing, frontend, components) also live there, under `.ai/rules/boost` — this is not just recorded decisions, it is load-bearing guidance you have not seen inline. Before you enter plan mode or create/edit any file, you MUST first: open @.ai/rules/index.md (it maps file globs to rule files), read every rule file whose globs cover the path(s) in scope, and run `grep -rin 'keyword' .ai/rules` to catch what a path match alone misses. Do not write code until you have read and are following every matching rule. If `.ai/rules` does not exist, continue without it. +- Record durable rules with `record-rule` so the next agent or teammate inherits them instead of working them out again. Pass a `glob` (e.g. `app/Http/Controllers/**`), a short `title`, and a few-line `note`. Always use `record-rule`, never your native memory or notes tool — native memory is personal and session-scoped; only `.ai/rules` is shared with the team and persists in the repo. + +## Artisan + +- Run Artisan commands directly via the command line (e.g., `php artisan route:list`). Use `php artisan list` to discover available commands and `php artisan [command] --help` to check parameters. +- Inspect routes with `php artisan route:list`. Filter with: `--method=GET`, `--name=users`, `--path=api`, `--except-vendor`, `--only-vendor`. +- Read configuration values using dot notation: `php artisan config:show app.name`, `php artisan config:show database.default`. Or read config files directly from the `config/` directory. + +## Tinker + +- Execute PHP in app context for debugging and testing code. Do not create models without user approval, prefer tests with factories instead. Prefer existing Artisan commands over custom tinker code. +- Always use single quotes to prevent shell expansion: `php artisan tinker --execute 'Your::code();'` + - Double quotes for PHP strings inside: `php artisan tinker --execute 'User::where("active", true)->count();'` + +=== php rules === + +# PHP + +- Always use curly braces for control structures, even for single-line bodies. +- Use PHP 8 constructor property promotion: `public function __construct(public GitHub $github) { }`. Do not leave empty zero-parameter `__construct()` methods unless the constructor is private. +- Use explicit return type declarations and type hints for all method parameters: `function isAccessible(User $user, ?string $path = null): bool` +- Use TitleCase for Enum keys: `FavoritePerson`, `BestLake`, `Monthly`. +- Prefer PHPDoc blocks over inline comments. Only add inline comments for exceptionally complex logic. +- Use array shape type definitions in PHPDoc blocks. + +=== deployments rules === + +# Deployment + +- Laravel can be deployed using [Laravel Cloud](https://cloud.laravel.com/), which is the fastest way to deploy and scale production Laravel applications. + +=== tests rules === + +# Test Enforcement + +- Every change must be programmatically tested. Write a new test or update an existing test, then run the affected tests to make sure they pass. +- Run the minimum number of tests needed to ensure code quality and speed. Use `php artisan test --compact` with a specific filename or filter. + +=== laravel/core rules === + +# Do Things the Laravel Way + +- Use `php artisan make:` commands to create new files (i.e. migrations, controllers, models, etc.). You can list available Artisan commands using `php artisan list` and check their parameters with `php artisan [command] --help`. +- If you're creating a generic PHP class, use `php artisan make:class`. +- Pass `--no-interaction` to all Artisan commands to ensure they work without user input. You should also pass the correct `--options` to ensure correct behavior. + +### Model Creation + +- When creating new models, create useful factories and seeders for them too. Ask the user if they need any other things, using `php artisan make:model --help` to check the available options. + +## APIs & Eloquent Resources + +- For APIs, default to using Eloquent API Resources and API versioning unless existing API routes do not, then you should follow existing application convention. + +## URL Generation + +- When generating links to other pages, prefer named routes and the `route()` function. + +## Testing + +- When creating models for tests, use the factories for the models. Check if the factory has custom states that can be used before manually setting up the model. +- Faker: Use methods such as `$this->faker->word()` or `fake()->randomDigit()`. Follow existing conventions whether to use `$this->faker` or `fake()`. +- When creating tests, make use of `php artisan make:test [options] {name}` to create a feature test, and pass `--unit` to create a unit test. Most tests should be feature tests. + +## Vite Error + +- If you receive an "Illuminate\Foundation\ViteException: Unable to locate file in Vite manifest" error, you can run `npm run build` or ask the user to run `npm run dev` or `composer run dev`. + +=== livewire/core rules === + +# Livewire + +- Livewire allow to build dynamic, reactive interfaces in PHP without writing JavaScript. +- You can use Alpine.js for client-side interactions instead of JavaScript frameworks. +- Keep state server-side so the UI reflects it. Validate and authorize in actions as you would in HTTP requests. + +=== pint/core rules === + +# Laravel Pint Code Formatter + +- If you have modified any PHP files, you must run `vendor/bin/pint --dirty --format agent` before finalizing changes to ensure your code matches the project's expected style. +- Do not run `vendor/bin/pint --test --format agent`, simply run `vendor/bin/pint --format agent` to fix any formatting issues. + +=== phpunit/core rules === + +# PHPUnit + +- This application uses PHPUnit for testing. All tests must be written as PHPUnit classes. Use `php artisan make:test --phpunit {name}` to create a new test. +- If you see a test using "Pest", convert it to PHPUnit. +- Every time a test has been updated, run that singular test. +- When the tests relating to your feature are passing, ask the user if they would like to also run the entire test suite to make sure everything is still passing. +- Tests should cover all happy paths, failure paths, and edge cases. +- You must not remove any tests or test files from the tests directory without approval. These are not temporary or helper files; these are core to the application. + +## Running Tests + +- Run the minimal number of tests, using an appropriate filter, before finalizing. +- To run all tests: `php artisan test --compact`. +- To run all tests in a file: `php artisan test --compact tests/Feature/ExampleTest.php`. +- To filter on a particular test name: `php artisan test --compact --filter=testName` (recommended after making a change to a related file). + + diff --git a/CLAUDE.md b/CLAUDE.md index 90644f3..c6f1b63 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -104,6 +104,13 @@ This project has domain-specific skills available in `**/skills/**`. You MUST ac - Laravel can be deployed using [Laravel Cloud](https://cloud.laravel.com/), which is the fastest way to deploy and scale production Laravel applications. +=== tests rules === + +# Test Enforcement + +- Every change must be programmatically tested. Write a new test or update an existing test, then run the affected tests to make sure they pass. +- Run the minimum number of tests needed to ensure code quality and speed. Use `php artisan test --compact` with a specific filename or filter. + === laravel/core rules === # Do Things the Laravel Way @@ -134,6 +141,14 @@ This project has domain-specific skills available in `**/skills/**`. You MUST ac - If you receive an "Illuminate\Foundation\ViteException: Unable to locate file in Vite manifest" error, you can run `npm run build` or ask the user to run `npm run dev` or `composer run dev`. +=== livewire/core rules === + +# Livewire + +- Livewire allow to build dynamic, reactive interfaces in PHP without writing JavaScript. +- You can use Alpine.js for client-side interactions instead of JavaScript frameworks. +- Keep state server-side so the UI reflects it. Validate and authorize in actions as you would in HTTP requests. + === pint/core rules === # Laravel Pint Code Formatter diff --git a/boost.json b/boost.json index c17819f..f785134 100644 --- a/boost.json +++ b/boost.json @@ -1,4 +1,8 @@ { + "agents": [ + "claude_code", + "cursor" + ], "cloud": false, "guidelines": true, "mcp": true, @@ -7,7 +11,11 @@ "skills": [ "infer-conventions", "ai-sdk-development", + "fortify-development", "laravel-best-practices", + "fluxui-development", + "livewire-development", + "echo-development", "tailwindcss-development" ] }