From 08bb42979a279220bff2bc57ad6c7069144165a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Falc=C3=B3n?= Date: Fri, 12 Jun 2026 18:20:30 +0200 Subject: [PATCH] chore: update Laravel Boost skills and guidelines (#521) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Output of running `php artisan boost:update`. - Add `cashier-stripe-development` skill - Rename `developing-with-fortify` → `fortify-development` - Refresh skill files, `AGENTS.md`, and `CLAUDE.md` guidelines - Update `boost.json` skill list ## Test plan No code changes — this only updates Boost-managed agent guideline files (`.agents/`, `.claude/`, `.github/` skills, `AGENTS.md`, `CLAUDE.md`, `boost.json`). --- .../cashier-stripe-development/SKILL.md | 108 ++++++++++++++ .../references/subscriptions.md | 108 ++++++++++++++ .../references/testing.md | 52 +++++++ .../references/webhooks.md | 132 ++++++++++++++++++ .../SKILL.md | 2 +- .../skills/inertia-react-development/SKILL.md | 82 ++++------- .agents/skills/pennant-development/SKILL.md | 2 +- .agents/skills/pest-testing/SKILL.md | 12 +- .../skills/tailwindcss-development/SKILL.md | 12 +- .agents/skills/wayfinder-development/SKILL.md | 7 - .../cashier-stripe-development/SKILL.md | 108 ++++++++++++++ .../references/subscriptions.md | 108 ++++++++++++++ .../references/testing.md | 52 +++++++ .../references/webhooks.md | 132 ++++++++++++++++++ .../SKILL.md | 2 +- .../skills/inertia-react-development/SKILL.md | 82 ++++------- .claude/skills/pennant-development/SKILL.md | 2 +- .claude/skills/pest-testing/SKILL.md | 12 +- .../skills/tailwindcss-development/SKILL.md | 12 +- .claude/skills/wayfinder-development/SKILL.md | 7 - .../cashier-stripe-development/SKILL.md | 108 ++++++++++++++ .../references/subscriptions.md | 108 ++++++++++++++ .../references/testing.md | 52 +++++++ .../references/webhooks.md | 132 ++++++++++++++++++ .../SKILL.md | 2 +- .../skills/inertia-react-development/SKILL.md | 82 ++++------- .github/skills/pennant-development/SKILL.md | 2 +- .github/skills/pest-testing/SKILL.md | 12 +- .../skills/tailwindcss-development/SKILL.md | 12 +- .github/skills/wayfinder-development/SKILL.md | 7 - AGENTS.md | 82 +++-------- CLAUDE.md | 79 +++-------- boost.json | 3 +- 33 files changed, 1350 insertions(+), 365 deletions(-) create mode 100644 .agents/skills/cashier-stripe-development/SKILL.md create mode 100644 .agents/skills/cashier-stripe-development/references/subscriptions.md create mode 100644 .agents/skills/cashier-stripe-development/references/testing.md create mode 100644 .agents/skills/cashier-stripe-development/references/webhooks.md rename .agents/skills/{developing-with-fortify => fortify-development}/SKILL.md (99%) create mode 100644 .claude/skills/cashier-stripe-development/SKILL.md create mode 100644 .claude/skills/cashier-stripe-development/references/subscriptions.md create mode 100644 .claude/skills/cashier-stripe-development/references/testing.md create mode 100644 .claude/skills/cashier-stripe-development/references/webhooks.md rename .claude/skills/{developing-with-fortify => fortify-development}/SKILL.md (99%) create mode 100644 .github/skills/cashier-stripe-development/SKILL.md create mode 100644 .github/skills/cashier-stripe-development/references/subscriptions.md create mode 100644 .github/skills/cashier-stripe-development/references/testing.md create mode 100644 .github/skills/cashier-stripe-development/references/webhooks.md rename .github/skills/{developing-with-fortify => fortify-development}/SKILL.md (99%) diff --git a/.agents/skills/cashier-stripe-development/SKILL.md b/.agents/skills/cashier-stripe-development/SKILL.md new file mode 100644 index 00000000..355e8a92 --- /dev/null +++ b/.agents/skills/cashier-stripe-development/SKILL.md @@ -0,0 +1,108 @@ +--- +name: cashier-stripe-development +description: "Handles Laravel Cashier Stripe integration including subscriptions, webhooks, Stripe Checkout, invoices, charges, refunds, trials, coupons, metered billing, and payment failure handling. Triggered when a user mentions Cashier, Billable, IncompletePayment, stripe_id, newSubscription, Stripe subscriptions, or billing. Also applies when setting up webhooks, handling SCA/3DS payment failures, testing with Stripe test cards, or troubleshooting incomplete subscriptions, CSRF webhook errors, or migration publish issues." +license: MIT +metadata: + author: laravel +--- + +# Cashier Stripe Development + +## When to Apply + +Activate this skill when: + +- Installing or configuring Laravel Cashier Stripe +- Setting up subscriptions, trials, quantities, or plan swapping +- Handling webhooks or SCA/3DS payment failures +- Working with Stripe Checkout, invoices, or charges +- Testing billing scenarios with Stripe test cards or tokens + +## Documentation + +Use `search-docs` for detailed Cashier patterns and documentation covering subscriptions, webhooks, Stripe Checkout, invoices, payment methods, and testing. + +For deeper guidance on specific topics, read the relevant reference file before implementing: + +- `references/subscriptions.md` covers subscription creation, status checks, swapping, trials, quantities, and multiple products +- `references/webhooks.md` covers webhook setup, custom handlers, CSRF exclusion, and local development with the Stripe CLI +- `references/testing.md` covers Stripe test cards, payment method tokens, and feature test patterns + +## Basic Usage + +### Installation + +```bash +php artisan vendor:publish --tag="cashier-migrations" +php artisan migrate +php artisan vendor:publish --tag="cashier-config" +``` + +### Environment Variables + +``` +STRIPE_KEY=pk_test_... +STRIPE_SECRET=sk_test_... +STRIPE_WEBHOOK_SECRET=whsec_... +CASHIER_CURRENCY=usd +CASHIER_CURRENCY_LOCALE=en_US +``` + +### Billable Model + + +```php +use Laravel\Cashier\Billable; + +class User extends Authenticatable +{ + use Billable; +} +``` + +For a non-User model, register it in a service provider: + + +```php +// In AppServiceProvider::boot() +Cashier::useCustomerModel(Team::class); +``` + +### Creating a Subscription + + +```php +use Laravel\Cashier\Exceptions\IncompletePayment; + +try { + $user->newSubscription('default', 'price_xxxx')->create($paymentMethodId); +} catch (IncompletePayment $e) { + return redirect()->route('cashier.payment', [$e->payment->id, 'redirect' => route('home')]); +} +``` + +Always wrap subscription creation in a try/catch for `IncompletePayment`. When a card requires 3DS authentication, Cashier throws this exception. The `cashier.payment` route is auto-registered and handles the confirmation flow. + +## Verification + +1. Run migrations and confirm `stripe_id`, `pm_type`, `pm_last_four`, and `trial_ends_at` columns exist on the billable model table +2. Test the webhook endpoint with `stripe listen --forward-to localhost/stripe/webhook` if you use the default path, or swap `stripe` for your configured `CASHIER_PATH` +3. Confirm `$user->subscribed('default')` returns the expected value for active and incomplete subscriptions + +## Common Pitfalls + +- The migration publish tag is `cashier-migrations`, not `cashier`. Running `migrate` before publishing results in missing columns and tables. +- `CASHIER_CURRENCY` must be set explicitly. It defaults to USD, which silently breaks non-US apps. +- The Stripe CLI generates its own webhook signing secret. It is different from the Dashboard endpoint secret. Using the wrong one causes signature verification failures. +- The webhook route must be excluded from CSRF verification using your configured `cashier.path`. If you change `CASHIER_PATH` from `stripe` to `billing`, exclude `billing/*`, not `stripe/*`. +- `canceled()` returns true as soon as `cancel()` is called, but the user still has access during the grace period. Use `ended()` to confirm access is fully revoked. +- `subscribed()` returns true during the grace period even though the subscription is canceled. +- `subscribed()` returns false for `incomplete` and `past_due` subscriptions by default. +- Prices cannot be swapped and quantity cannot be updated while a subscription has an incomplete payment. +- When extending `WebhookController`, call `Cashier::ignoreRoutes()` in a service provider and re-register both `cashier.payment` and `cashier.webhook` under the configured `cashier.path`. +- Use `Cashier::useCustomerModel()` in a service provider to set a custom billable model. There is no `CASHIER_MODEL` env var. +- `trial_ends_at` is a local database column synced via webhooks. It will be stale if webhooks are not configured in production. +- In MySQL, the `stripe_id` column must use `utf8_bin` collation to avoid case-sensitivity issues. +- `noProrate()` has no effect when combined with `swapAndInvoice()`. That method always prorates. +- Methods like `withPromotionCode()` require the Stripe API ID such as `promo_xxxx`, not the customer-facing code. Use `findPromotionCode()` to resolve a code to its ID. +- Always use `search-docs` for the latest Cashier documentation rather than relying on this skill alone. \ No newline at end of file diff --git a/.agents/skills/cashier-stripe-development/references/subscriptions.md b/.agents/skills/cashier-stripe-development/references/subscriptions.md new file mode 100644 index 00000000..ad7113ea --- /dev/null +++ b/.agents/skills/cashier-stripe-development/references/subscriptions.md @@ -0,0 +1,108 @@ +# Subscriptions Reference + +Use `search-docs` for authoritative documentation on subscriptions. + +## Status Checks + +| Method | Returns true when | +|---|---| +| `$user->subscribed('default')` | Active or on grace period | +| `->onTrial()` | Trial period active | +| `->onGracePeriod()` | Canceled, period not yet ended | +| `->canceled()` | `ends_at` is set, may still have access | +| `->ended()` | Canceled and grace period expired | +| `->incomplete()` | Awaiting SCA/3DS confirmation | +| `->pastDue()` | Payment overdue | +| `->recurring()` | Active and not on trial | + +Check by product or price: + +```php +$user->subscribedToProduct('prod_premium', 'default'); +$user->subscribedToPrice('price_monthly', 'default'); +``` + +## Swapping Plans + +```php +$user->subscription('default')->swap('price_new'); +$user->subscription('default')->noProrate()->swap('price_new'); +$user->subscription('default')->swapAndInvoice('price_new'); +$user->subscription('default')->skipTrial()->swap('price_new'); +``` + +## Quantity + +```php +$user->subscription('default')->incrementQuantity(); +$user->subscription('default')->decrementQuantity(); +$user->subscription('default')->updateQuantity(10); +$user->subscription('default')->noProrate()->updateQuantity(10); +``` + +## Trials + +```php +$user->newSubscription('default', 'price_xxxx') + ->trialDays(14) + ->create($paymentMethodId); + +$subscription->extendTrial(now()->addDays(7)); +``` + +## Multiple Products on One Subscription + +```php +$user->newSubscription('default', ['price_monthly', 'price_chat']) + ->quantity(5, 'price_chat') + ->create($paymentMethod); + +$user->subscription('default')->addPrice('price_chat'); +$user->subscription('default')->removePrice('price_chat'); +$user->subscription('default')->swap(['price_pro', 'price_chat']); +``` + +## Multiple Subscriptions + +```php +$user->newSubscription('swimming', 'price_swimming_monthly')->create($pm); +$user->newSubscription('gym', 'price_gym_monthly')->create($pm); + +$user->subscription('swimming')->swap('price_swimming_yearly'); +$user->subscription('gym')->cancel(); +``` + +## Cancellation and Resumption + +```php +$user->subscription('default')->cancel(); // At end of billing period +$user->subscription('default')->cancelNow(); // Immediately +$user->subscription('default')->resume(); // During grace period only +``` + +## Incomplete Payment Handling + +```php +if ($user->hasIncompletePayment('default')) { + $paymentId = $user->subscription('default')->latestPayment()->id; + return redirect()->route('cashier.payment', $paymentId); +} +``` + +Opt out of default deactivation behavior: + +```php +Cashier::keepPastDueSubscriptionsActive(); +Cashier::keepIncompleteSubscriptionsActive(); +``` + +## Metered / Usage-Based Billing + +```php +$user->newSubscription('default') + ->meteredPrice('price_metered') + ->create($paymentMethodId); + +$user->reportMeterEvent('emails-sent'); +$user->reportMeterEvent('emails-sent', quantity: 15); +``` \ No newline at end of file diff --git a/.agents/skills/cashier-stripe-development/references/testing.md b/.agents/skills/cashier-stripe-development/references/testing.md new file mode 100644 index 00000000..97e91f91 --- /dev/null +++ b/.agents/skills/cashier-stripe-development/references/testing.md @@ -0,0 +1,52 @@ +# Testing Reference + +Use `search-docs` for authoritative documentation on testing Cashier integrations. + +## Test Cards and Tokens + +Use card numbers for browser-based flows (Stripe.js / Checkout). Use `pm_card_*` tokens directly in feature tests that call the Stripe API. + +| Card Number | Token | Behavior | +|---|---|---| +| `4242 4242 4242 4242` | `pm_card_visa` | Succeeds immediately | +| `4000 0025 0000 3155` | `pm_card_threeDSecure2Required` | Requires SCA/3DS | +| `4000 0027 6000 3184` | `pm_card_authenticationRequired` | Requires authentication | +| `4000 0000 0000 9995` | `pm_card_chargeDeclinedInsufficientFunds` | Declined, insufficient funds | +| `4000 0000 0000 0002` | `pm_card_chargeDeclined` | Declined | + +Use expiry `12/34`, any CVC, any ZIP for card number inputs. + +## Feature Test Example + +Feature tests that hit the real Stripe test API use `pm_card_*` tokens: + +```php +public function test_user_can_subscribe(): void +{ + $user = User::factory()->create(); + + $user->newSubscription('default', 'price_xxxx') + ->create('pm_card_visa'); + + $this->assertTrue($user->subscribed('default')); +} + +public function test_incomplete_payment_is_handled(): void +{ + $user = User::factory()->create(); + + try { + $user->newSubscription('default', 'price_xxxx') + ->create('pm_card_threeDSecure2Required'); + } catch (\Laravel\Cashier\Exceptions\IncompletePayment $e) { + $this->assertTrue($user->subscription('default')->incomplete()); + } +} +``` + +## Setup Notes + +- Use Stripe test mode keys (`sk_test_...`, `pk_test_...`) in your test environment +- Cashier does not ship a global `fake()` helper. Tests hit the real Stripe test API by default. +- Refer to `tests/Feature/` in the Cashier package itself for integration test patterns covering subscription creation, payment methods, and webhook handling +- Use `search-docs` for current guidance on mocking Stripe HTTP calls or using Stripe's test clock feature for time-sensitive scenarios \ No newline at end of file diff --git a/.agents/skills/cashier-stripe-development/references/webhooks.md b/.agents/skills/cashier-stripe-development/references/webhooks.md new file mode 100644 index 00000000..19d2f708 --- /dev/null +++ b/.agents/skills/cashier-stripe-development/references/webhooks.md @@ -0,0 +1,132 @@ +# Webhooks Reference + +Use `search-docs` for authoritative documentation on webhooks. + +## Auto-Registered Routes + +Cashier registers two routes automatically under the `cashier.path` prefix (`config('cashier.path')`, default `stripe`): + +- `POST /{cashier.path}/webhook` named `cashier.webhook` +- `GET /{cashier.path}/payment/{id}` named `cashier.payment` + +With the default config these are `/stripe/webhook` and `/stripe/payment/{id}`. If you set `CASHIER_PATH=billing`, they become `/billing/webhook` and `/billing/payment/{id}`. + +## CSRF Exclusion + +Use the same path prefix you configured for Cashier here. If `CASHIER_PATH=billing`, exclude `billing/*` instead of `stripe/*`. + +**Laravel 11+ (`bootstrap/app.php`, default path example):** + +```php +->withMiddleware(function (Middleware $middleware) { + $middleware->validateCsrfTokens(except: ['stripe/*']); +}) +``` + +**Laravel 10 (`app/Http/Middleware/VerifyCsrfToken.php`, default path example):** + +```php +protected $except = [ + 'stripe/*', +]; +``` + +## Local Development with Stripe CLI + +If you changed `cashier.path`, forward Stripe CLI events to that URL instead of `/stripe/webhook`. + +```bash +stripe login +stripe listen --forward-to your-app.test/stripe/webhook +stripe trigger invoice.payment_succeeded +``` + +The CLI outputs a `whsec_...` signing secret specific to that session. Set it as `STRIPE_WEBHOOK_SECRET` locally. It is not the same as the Dashboard endpoint secret. + +## Registering Events in the Stripe Dashboard + +Use the Artisan command to create the endpoint automatically with all required events: + +```bash +php artisan cashier:webhook +``` + +Cashier's `cashier:webhook` command registers these events by default: + +- `customer.subscription.created` +- `customer.subscription.updated` +- `customer.subscription.deleted` +- `customer.updated` / `customer.deleted` +- `invoice.payment_action_required` +- `invoice.payment_succeeded` +- `payment_method.automatically_updated` + +Cashier's `WebhookController` has built-in handlers for all of the above except `invoice.payment_succeeded`. For renewal hooks, prefer `WebhookReceived` / `WebhookHandled` listeners unless you intentionally add your own controller method. + +## Custom Handlers: Extending WebhookController + +Method name pattern: `handle` + StudlyCase of event type with dots replaced by underscores. + +`customer.subscription.created` becomes `handleCustomerSubscriptionCreated`. + +```php +use Laravel\Cashier\Http\Controllers\WebhookController as CashierController; + +class StripeWebhookController extends CashierController +{ + public function handleCustomerSubscriptionCreated(array $payload) + { + $response = parent::handleCustomerSubscriptionCreated($payload); + + // your logic after Cashier syncs the subscription + + return $response; + } +} +``` + +If you add a method for an event Cashier does not handle internally, such as `invoice.payment_succeeded`, do not call `parent::handle...()` unless the base controller actually defines that method. + +In a service provider, disable auto-registration and re-register both Cashier routes so the incomplete-payment flow and `cashier:webhook` command keep working: + +```php +Cashier::ignoreRoutes(); +``` + +```php +// routes/web.php +use App\Http\Controllers\StripeWebhookController; +use Illuminate\Support\Facades\Route; +use Laravel\Cashier\Http\Controllers\PaymentController; + +Route::prefix(config('cashier.path')) + ->name('cashier.') + ->group(function () { + Route::get('payment/{id}', [PaymentController::class, 'show'])->name('payment'); + Route::post('webhook', [StripeWebhookController::class, 'handleWebhook'])->name('webhook'); + }); +``` + +Keep the `cashier.webhook` route name unless you plan to pass `--url` explicitly to `php artisan cashier:webhook`. + +## Custom Handlers: Listening to Events + +The simpler option when you do not need to replace Cashier's internal logic, or when you want to react to events such as `invoice.payment_succeeded` that Cashier does not process itself: + +```php +use Laravel\Cashier\Events\WebhookReceived; +use Laravel\Cashier\Events\WebhookHandled; + +// WebhookReceived fires for every event before Cashier processes it +// WebhookHandled fires after Cashier processes it + +Event::listen(WebhookReceived::class, function (WebhookReceived $event) { + if ($event->payload['type'] === 'invoice.payment_succeeded') { + // handle renewal + } +}); +``` + +## Signature Verification + +`VerifyWebhookSignature` middleware is applied automatically when `cashier.webhook.secret` is set. No extra wiring is needed. \ No newline at end of file diff --git a/.agents/skills/developing-with-fortify/SKILL.md b/.agents/skills/fortify-development/SKILL.md similarity index 99% rename from .agents/skills/developing-with-fortify/SKILL.md rename to .agents/skills/fortify-development/SKILL.md index a09178e3..bd05dd6d 100644 --- a/.agents/skills/developing-with-fortify/SKILL.md +++ b/.agents/skills/fortify-development/SKILL.md @@ -1,5 +1,5 @@ --- -name: developing-with-fortify +name: fortify-development description: Laravel Fortify headless authentication backend development. Activate when implementing authentication features including login, registration, password reset, email verification, two-factor authentication (2FA/TOTP), profile updates, headless auth, authentication scaffolding, or auth guards in Laravel applications. --- diff --git a/.agents/skills/inertia-react-development/SKILL.md b/.agents/skills/inertia-react-development/SKILL.md index 5fe2712d..bb01fd9c 100644 --- a/.agents/skills/inertia-react-development/SKILL.md +++ b/.agents/skills/inertia-react-development/SKILL.md @@ -15,7 +15,7 @@ Activate this skill when: - Creating or modifying React page components for Inertia - Working with forms in React (using `
` or `useForm`) - Implementing client-side navigation with `` or `router` -- Using v2 features: deferred props, prefetching, or polling +- Using v2 features: deferred props, prefetching, WhenVisible, InfiniteScroll, once props, flash data, or polling - Building React-specific features with the Inertia protocol ## Documentation @@ -295,14 +295,21 @@ export default function UsersIndex({ users }) { ### Polling -Use the `usePoll` hook to automatically refresh data at intervals. It handles cleanup on unmount and throttles polling when the tab is inactive. +Automatically refresh data at intervals: - + ```react -import { usePoll } from '@inertiajs/react' +import { router } from '@inertiajs/react' +import { useEffect } from 'react' export default function Dashboard({ stats }) { - usePoll(5000) + useEffect(() => { + const interval = setInterval(() => { + router.reload({ only: ['stats'] }) + }, 5000) // Poll every 5 seconds + + return () => clearInterval(interval) + }, []) return (
@@ -313,65 +320,38 @@ export default function Dashboard({ stats }) { } ``` - -```react -import { usePoll } from '@inertiajs/react' +### WhenVisible -export default function Dashboard({ stats }) { - const { start, stop } = usePoll(5000, { - only: ['stats'], - onStart() { - console.log('Polling request started') - }, - onFinish() { - console.log('Polling request finished') - }, - }, { - autoStart: false, - keepAlive: true, - }) +Lazy-load a prop when an element scrolls into view. Useful for deferring expensive data that sits below the fold: - return ( -
-

Dashboard

-
Active Users: {stats.activeUsers}
- - -
- ) -} -``` - -- `autoStart` (default `true`) — set to `false` to start polling manually via the returned `start()` function -- `keepAlive` (default `false`) — set to `true` to prevent throttling when the browser tab is inactive - -### WhenVisible (Infinite Scroll) - -Load more data when user scrolls to a specific element: - - + ```react import { WhenVisible } from '@inertiajs/react' -export default function UsersList({ users }) { +export default function Dashboard({ stats }) { return (
- {users.data.map(user => ( -
{user.name}
- ))} +

Dashboard

- {users.next_page_url && ( - Loading more...
} - /> - )} + {/* stats prop is loaded only when this section scrolls into view */} + Loading stats...
}> + {({ fetching }) => ( +
+

Total Users: {stats.total_users}

+

Revenue: {stats.revenue}

+ {fetching && Refreshing...} +
+ )} + ) } ``` +## Server-Side Patterns + +Server-side patterns (Inertia::render, props, middleware) are covered in inertia-laravel guidelines. + ## Common Pitfalls - Using traditional `` links instead of Inertia's `` component (breaks SPA behavior) diff --git a/.agents/skills/pennant-development/SKILL.md b/.agents/skills/pennant-development/SKILL.md index 76a8c82f..3b364661 100644 --- a/.agents/skills/pennant-development/SKILL.md +++ b/.agents/skills/pennant-development/SKILL.md @@ -1,6 +1,6 @@ --- name: pennant-development -description: "Manages feature flags with Laravel Pennant. Activates when creating, checking, or toggling feature flags; showing or hiding features conditionally; implementing A/B testing; working with @feature directive; or when the user mentions feature flags, feature toggles, Pennant, conditional features, rollouts, or gradually enabling features." +description: "Use when working with Laravel Pennant the official Laravel feature flag package. Trigger whenever the query mentions Pennant by name or involves feature flags or feature toggles in a Laravel project. Tasks include defining feature flags checking whether features are active creating class based features in `app/Features` using Blade `@feature` directives scoping flags to users or teams building custom Pennant storage drivers protecting routes with feature flags testing feature flags with Pest or PHPUnit and implementing A B testing or gradual rollouts with feature flags. Do not trigger for generic Laravel configuration authorization policies authentication or non Pennant feature management systems." license: MIT metadata: author: laravel diff --git a/.agents/skills/pest-testing/SKILL.md b/.agents/skills/pest-testing/SKILL.md index 56198610..ba774e71 100644 --- a/.agents/skills/pest-testing/SKILL.md +++ b/.agents/skills/pest-testing/SKILL.md @@ -1,6 +1,6 @@ --- name: pest-testing -description: "Tests applications using the Pest 4 PHP framework. Activates when writing tests, creating unit or feature tests, adding assertions, testing Livewire components, browser testing, debugging test failures, working with datasets or mocking; or when the user mentions test, spec, TDD, expects, assertion, coverage, or needs to verify functionality works." +description: "Use this skill for Pest PHP testing in Laravel projects only. Trigger whenever any test is being written, edited, fixed, or refactored — including fixing tests that broke after a code change, adding assertions, converting PHPUnit to Pest, adding datasets, and TDD workflows. Always activate when the user asks how to write something in Pest, mentions test files or directories (tests/Feature, tests/Unit, tests/Browser), or needs browser testing, smoke testing multiple pages for JS errors, or architecture tests. Covers: it()/expect() syntax, datasets, mocking, browser testing (visit/click/fill), smoke testing, arch(), Livewire component tests, RefreshDatabase, and all Pest 4 features. Do not use for factories, seeders, migrations, controllers, models, or non-test PHP code." license: MIT metadata: author: laravel @@ -8,16 +8,6 @@ metadata: # Pest Testing 4 -## When to Apply - -Activate this skill when: - -- Creating new tests (unit, feature, or browser) -- Modifying existing tests -- Debugging test failures -- Working with browser testing or smoke testing -- Writing architecture tests or visual regression tests - ## Documentation Use `search-docs` for detailed Pest 4 patterns and documentation. diff --git a/.agents/skills/tailwindcss-development/SKILL.md b/.agents/skills/tailwindcss-development/SKILL.md index 21a7e463..7c8e295e 100644 --- a/.agents/skills/tailwindcss-development/SKILL.md +++ b/.agents/skills/tailwindcss-development/SKILL.md @@ -1,6 +1,6 @@ --- name: tailwindcss-development -description: "Styles applications using Tailwind CSS v4 utilities. Activates when adding styles, restyling components, working with gradients, spacing, layout, flex, grid, responsive design, dark mode, colors, typography, or borders; or when the user mentions CSS, styling, classes, Tailwind, restyle, hero section, cards, buttons, or any visual/UI changes." +description: "Always invoke when the user's message includes 'tailwind' in any form. Also invoke for: building responsive grid layouts (multi-column card grids, product grids), flex/grid page structures (dashboards with sidebars, fixed topbars, mobile-toggle navs), styling UI components (cards, tables, navbars, pricing sections, forms, inputs, badges), adding dark mode variants, fixing spacing or typography, and Tailwind v3/v4 work. The core use case: writing or fixing Tailwind utility classes in HTML templates (Blade, JSX, Vue). Skip for backend PHP logic, database queries, API routes, JavaScript with no HTML/CSS component, CSS file audits, build tool configuration, and vanilla CSS." license: MIT metadata: author: laravel @@ -8,16 +8,6 @@ metadata: # Tailwind CSS Development -## When to Apply - -Activate this skill when: - -- Adding styles to components or pages -- Working with responsive design -- Implementing dark mode -- Extracting repeated patterns into components -- Debugging spacing or layout issues - ## Documentation Use `search-docs` for detailed Tailwind CSS v4 patterns and documentation. diff --git a/.agents/skills/wayfinder-development/SKILL.md b/.agents/skills/wayfinder-development/SKILL.md index 37d620d7..06ece8fb 100644 --- a/.agents/skills/wayfinder-development/SKILL.md +++ b/.agents/skills/wayfinder-development/SKILL.md @@ -8,13 +8,6 @@ metadata: # Wayfinder Development -## When to Apply - -Activate whenever referencing backend routes in frontend components: -- Importing from `@/actions/` or `@/routes/` -- Calling Laravel routes from TypeScript/JavaScript -- Creating links or navigation to backend endpoints - ## Documentation Use `search-docs` for detailed Wayfinder patterns and documentation. diff --git a/.claude/skills/cashier-stripe-development/SKILL.md b/.claude/skills/cashier-stripe-development/SKILL.md new file mode 100644 index 00000000..355e8a92 --- /dev/null +++ b/.claude/skills/cashier-stripe-development/SKILL.md @@ -0,0 +1,108 @@ +--- +name: cashier-stripe-development +description: "Handles Laravel Cashier Stripe integration including subscriptions, webhooks, Stripe Checkout, invoices, charges, refunds, trials, coupons, metered billing, and payment failure handling. Triggered when a user mentions Cashier, Billable, IncompletePayment, stripe_id, newSubscription, Stripe subscriptions, or billing. Also applies when setting up webhooks, handling SCA/3DS payment failures, testing with Stripe test cards, or troubleshooting incomplete subscriptions, CSRF webhook errors, or migration publish issues." +license: MIT +metadata: + author: laravel +--- + +# Cashier Stripe Development + +## When to Apply + +Activate this skill when: + +- Installing or configuring Laravel Cashier Stripe +- Setting up subscriptions, trials, quantities, or plan swapping +- Handling webhooks or SCA/3DS payment failures +- Working with Stripe Checkout, invoices, or charges +- Testing billing scenarios with Stripe test cards or tokens + +## Documentation + +Use `search-docs` for detailed Cashier patterns and documentation covering subscriptions, webhooks, Stripe Checkout, invoices, payment methods, and testing. + +For deeper guidance on specific topics, read the relevant reference file before implementing: + +- `references/subscriptions.md` covers subscription creation, status checks, swapping, trials, quantities, and multiple products +- `references/webhooks.md` covers webhook setup, custom handlers, CSRF exclusion, and local development with the Stripe CLI +- `references/testing.md` covers Stripe test cards, payment method tokens, and feature test patterns + +## Basic Usage + +### Installation + +```bash +php artisan vendor:publish --tag="cashier-migrations" +php artisan migrate +php artisan vendor:publish --tag="cashier-config" +``` + +### Environment Variables + +``` +STRIPE_KEY=pk_test_... +STRIPE_SECRET=sk_test_... +STRIPE_WEBHOOK_SECRET=whsec_... +CASHIER_CURRENCY=usd +CASHIER_CURRENCY_LOCALE=en_US +``` + +### Billable Model + + +```php +use Laravel\Cashier\Billable; + +class User extends Authenticatable +{ + use Billable; +} +``` + +For a non-User model, register it in a service provider: + + +```php +// In AppServiceProvider::boot() +Cashier::useCustomerModel(Team::class); +``` + +### Creating a Subscription + + +```php +use Laravel\Cashier\Exceptions\IncompletePayment; + +try { + $user->newSubscription('default', 'price_xxxx')->create($paymentMethodId); +} catch (IncompletePayment $e) { + return redirect()->route('cashier.payment', [$e->payment->id, 'redirect' => route('home')]); +} +``` + +Always wrap subscription creation in a try/catch for `IncompletePayment`. When a card requires 3DS authentication, Cashier throws this exception. The `cashier.payment` route is auto-registered and handles the confirmation flow. + +## Verification + +1. Run migrations and confirm `stripe_id`, `pm_type`, `pm_last_four`, and `trial_ends_at` columns exist on the billable model table +2. Test the webhook endpoint with `stripe listen --forward-to localhost/stripe/webhook` if you use the default path, or swap `stripe` for your configured `CASHIER_PATH` +3. Confirm `$user->subscribed('default')` returns the expected value for active and incomplete subscriptions + +## Common Pitfalls + +- The migration publish tag is `cashier-migrations`, not `cashier`. Running `migrate` before publishing results in missing columns and tables. +- `CASHIER_CURRENCY` must be set explicitly. It defaults to USD, which silently breaks non-US apps. +- The Stripe CLI generates its own webhook signing secret. It is different from the Dashboard endpoint secret. Using the wrong one causes signature verification failures. +- The webhook route must be excluded from CSRF verification using your configured `cashier.path`. If you change `CASHIER_PATH` from `stripe` to `billing`, exclude `billing/*`, not `stripe/*`. +- `canceled()` returns true as soon as `cancel()` is called, but the user still has access during the grace period. Use `ended()` to confirm access is fully revoked. +- `subscribed()` returns true during the grace period even though the subscription is canceled. +- `subscribed()` returns false for `incomplete` and `past_due` subscriptions by default. +- Prices cannot be swapped and quantity cannot be updated while a subscription has an incomplete payment. +- When extending `WebhookController`, call `Cashier::ignoreRoutes()` in a service provider and re-register both `cashier.payment` and `cashier.webhook` under the configured `cashier.path`. +- Use `Cashier::useCustomerModel()` in a service provider to set a custom billable model. There is no `CASHIER_MODEL` env var. +- `trial_ends_at` is a local database column synced via webhooks. It will be stale if webhooks are not configured in production. +- In MySQL, the `stripe_id` column must use `utf8_bin` collation to avoid case-sensitivity issues. +- `noProrate()` has no effect when combined with `swapAndInvoice()`. That method always prorates. +- Methods like `withPromotionCode()` require the Stripe API ID such as `promo_xxxx`, not the customer-facing code. Use `findPromotionCode()` to resolve a code to its ID. +- Always use `search-docs` for the latest Cashier documentation rather than relying on this skill alone. \ No newline at end of file diff --git a/.claude/skills/cashier-stripe-development/references/subscriptions.md b/.claude/skills/cashier-stripe-development/references/subscriptions.md new file mode 100644 index 00000000..ad7113ea --- /dev/null +++ b/.claude/skills/cashier-stripe-development/references/subscriptions.md @@ -0,0 +1,108 @@ +# Subscriptions Reference + +Use `search-docs` for authoritative documentation on subscriptions. + +## Status Checks + +| Method | Returns true when | +|---|---| +| `$user->subscribed('default')` | Active or on grace period | +| `->onTrial()` | Trial period active | +| `->onGracePeriod()` | Canceled, period not yet ended | +| `->canceled()` | `ends_at` is set, may still have access | +| `->ended()` | Canceled and grace period expired | +| `->incomplete()` | Awaiting SCA/3DS confirmation | +| `->pastDue()` | Payment overdue | +| `->recurring()` | Active and not on trial | + +Check by product or price: + +```php +$user->subscribedToProduct('prod_premium', 'default'); +$user->subscribedToPrice('price_monthly', 'default'); +``` + +## Swapping Plans + +```php +$user->subscription('default')->swap('price_new'); +$user->subscription('default')->noProrate()->swap('price_new'); +$user->subscription('default')->swapAndInvoice('price_new'); +$user->subscription('default')->skipTrial()->swap('price_new'); +``` + +## Quantity + +```php +$user->subscription('default')->incrementQuantity(); +$user->subscription('default')->decrementQuantity(); +$user->subscription('default')->updateQuantity(10); +$user->subscription('default')->noProrate()->updateQuantity(10); +``` + +## Trials + +```php +$user->newSubscription('default', 'price_xxxx') + ->trialDays(14) + ->create($paymentMethodId); + +$subscription->extendTrial(now()->addDays(7)); +``` + +## Multiple Products on One Subscription + +```php +$user->newSubscription('default', ['price_monthly', 'price_chat']) + ->quantity(5, 'price_chat') + ->create($paymentMethod); + +$user->subscription('default')->addPrice('price_chat'); +$user->subscription('default')->removePrice('price_chat'); +$user->subscription('default')->swap(['price_pro', 'price_chat']); +``` + +## Multiple Subscriptions + +```php +$user->newSubscription('swimming', 'price_swimming_monthly')->create($pm); +$user->newSubscription('gym', 'price_gym_monthly')->create($pm); + +$user->subscription('swimming')->swap('price_swimming_yearly'); +$user->subscription('gym')->cancel(); +``` + +## Cancellation and Resumption + +```php +$user->subscription('default')->cancel(); // At end of billing period +$user->subscription('default')->cancelNow(); // Immediately +$user->subscription('default')->resume(); // During grace period only +``` + +## Incomplete Payment Handling + +```php +if ($user->hasIncompletePayment('default')) { + $paymentId = $user->subscription('default')->latestPayment()->id; + return redirect()->route('cashier.payment', $paymentId); +} +``` + +Opt out of default deactivation behavior: + +```php +Cashier::keepPastDueSubscriptionsActive(); +Cashier::keepIncompleteSubscriptionsActive(); +``` + +## Metered / Usage-Based Billing + +```php +$user->newSubscription('default') + ->meteredPrice('price_metered') + ->create($paymentMethodId); + +$user->reportMeterEvent('emails-sent'); +$user->reportMeterEvent('emails-sent', quantity: 15); +``` \ No newline at end of file diff --git a/.claude/skills/cashier-stripe-development/references/testing.md b/.claude/skills/cashier-stripe-development/references/testing.md new file mode 100644 index 00000000..97e91f91 --- /dev/null +++ b/.claude/skills/cashier-stripe-development/references/testing.md @@ -0,0 +1,52 @@ +# Testing Reference + +Use `search-docs` for authoritative documentation on testing Cashier integrations. + +## Test Cards and Tokens + +Use card numbers for browser-based flows (Stripe.js / Checkout). Use `pm_card_*` tokens directly in feature tests that call the Stripe API. + +| Card Number | Token | Behavior | +|---|---|---| +| `4242 4242 4242 4242` | `pm_card_visa` | Succeeds immediately | +| `4000 0025 0000 3155` | `pm_card_threeDSecure2Required` | Requires SCA/3DS | +| `4000 0027 6000 3184` | `pm_card_authenticationRequired` | Requires authentication | +| `4000 0000 0000 9995` | `pm_card_chargeDeclinedInsufficientFunds` | Declined, insufficient funds | +| `4000 0000 0000 0002` | `pm_card_chargeDeclined` | Declined | + +Use expiry `12/34`, any CVC, any ZIP for card number inputs. + +## Feature Test Example + +Feature tests that hit the real Stripe test API use `pm_card_*` tokens: + +```php +public function test_user_can_subscribe(): void +{ + $user = User::factory()->create(); + + $user->newSubscription('default', 'price_xxxx') + ->create('pm_card_visa'); + + $this->assertTrue($user->subscribed('default')); +} + +public function test_incomplete_payment_is_handled(): void +{ + $user = User::factory()->create(); + + try { + $user->newSubscription('default', 'price_xxxx') + ->create('pm_card_threeDSecure2Required'); + } catch (\Laravel\Cashier\Exceptions\IncompletePayment $e) { + $this->assertTrue($user->subscription('default')->incomplete()); + } +} +``` + +## Setup Notes + +- Use Stripe test mode keys (`sk_test_...`, `pk_test_...`) in your test environment +- Cashier does not ship a global `fake()` helper. Tests hit the real Stripe test API by default. +- Refer to `tests/Feature/` in the Cashier package itself for integration test patterns covering subscription creation, payment methods, and webhook handling +- Use `search-docs` for current guidance on mocking Stripe HTTP calls or using Stripe's test clock feature for time-sensitive scenarios \ No newline at end of file diff --git a/.claude/skills/cashier-stripe-development/references/webhooks.md b/.claude/skills/cashier-stripe-development/references/webhooks.md new file mode 100644 index 00000000..19d2f708 --- /dev/null +++ b/.claude/skills/cashier-stripe-development/references/webhooks.md @@ -0,0 +1,132 @@ +# Webhooks Reference + +Use `search-docs` for authoritative documentation on webhooks. + +## Auto-Registered Routes + +Cashier registers two routes automatically under the `cashier.path` prefix (`config('cashier.path')`, default `stripe`): + +- `POST /{cashier.path}/webhook` named `cashier.webhook` +- `GET /{cashier.path}/payment/{id}` named `cashier.payment` + +With the default config these are `/stripe/webhook` and `/stripe/payment/{id}`. If you set `CASHIER_PATH=billing`, they become `/billing/webhook` and `/billing/payment/{id}`. + +## CSRF Exclusion + +Use the same path prefix you configured for Cashier here. If `CASHIER_PATH=billing`, exclude `billing/*` instead of `stripe/*`. + +**Laravel 11+ (`bootstrap/app.php`, default path example):** + +```php +->withMiddleware(function (Middleware $middleware) { + $middleware->validateCsrfTokens(except: ['stripe/*']); +}) +``` + +**Laravel 10 (`app/Http/Middleware/VerifyCsrfToken.php`, default path example):** + +```php +protected $except = [ + 'stripe/*', +]; +``` + +## Local Development with Stripe CLI + +If you changed `cashier.path`, forward Stripe CLI events to that URL instead of `/stripe/webhook`. + +```bash +stripe login +stripe listen --forward-to your-app.test/stripe/webhook +stripe trigger invoice.payment_succeeded +``` + +The CLI outputs a `whsec_...` signing secret specific to that session. Set it as `STRIPE_WEBHOOK_SECRET` locally. It is not the same as the Dashboard endpoint secret. + +## Registering Events in the Stripe Dashboard + +Use the Artisan command to create the endpoint automatically with all required events: + +```bash +php artisan cashier:webhook +``` + +Cashier's `cashier:webhook` command registers these events by default: + +- `customer.subscription.created` +- `customer.subscription.updated` +- `customer.subscription.deleted` +- `customer.updated` / `customer.deleted` +- `invoice.payment_action_required` +- `invoice.payment_succeeded` +- `payment_method.automatically_updated` + +Cashier's `WebhookController` has built-in handlers for all of the above except `invoice.payment_succeeded`. For renewal hooks, prefer `WebhookReceived` / `WebhookHandled` listeners unless you intentionally add your own controller method. + +## Custom Handlers: Extending WebhookController + +Method name pattern: `handle` + StudlyCase of event type with dots replaced by underscores. + +`customer.subscription.created` becomes `handleCustomerSubscriptionCreated`. + +```php +use Laravel\Cashier\Http\Controllers\WebhookController as CashierController; + +class StripeWebhookController extends CashierController +{ + public function handleCustomerSubscriptionCreated(array $payload) + { + $response = parent::handleCustomerSubscriptionCreated($payload); + + // your logic after Cashier syncs the subscription + + return $response; + } +} +``` + +If you add a method for an event Cashier does not handle internally, such as `invoice.payment_succeeded`, do not call `parent::handle...()` unless the base controller actually defines that method. + +In a service provider, disable auto-registration and re-register both Cashier routes so the incomplete-payment flow and `cashier:webhook` command keep working: + +```php +Cashier::ignoreRoutes(); +``` + +```php +// routes/web.php +use App\Http\Controllers\StripeWebhookController; +use Illuminate\Support\Facades\Route; +use Laravel\Cashier\Http\Controllers\PaymentController; + +Route::prefix(config('cashier.path')) + ->name('cashier.') + ->group(function () { + Route::get('payment/{id}', [PaymentController::class, 'show'])->name('payment'); + Route::post('webhook', [StripeWebhookController::class, 'handleWebhook'])->name('webhook'); + }); +``` + +Keep the `cashier.webhook` route name unless you plan to pass `--url` explicitly to `php artisan cashier:webhook`. + +## Custom Handlers: Listening to Events + +The simpler option when you do not need to replace Cashier's internal logic, or when you want to react to events such as `invoice.payment_succeeded` that Cashier does not process itself: + +```php +use Laravel\Cashier\Events\WebhookReceived; +use Laravel\Cashier\Events\WebhookHandled; + +// WebhookReceived fires for every event before Cashier processes it +// WebhookHandled fires after Cashier processes it + +Event::listen(WebhookReceived::class, function (WebhookReceived $event) { + if ($event->payload['type'] === 'invoice.payment_succeeded') { + // handle renewal + } +}); +``` + +## Signature Verification + +`VerifyWebhookSignature` middleware is applied automatically when `cashier.webhook.secret` is set. No extra wiring is needed. \ No newline at end of file diff --git a/.claude/skills/developing-with-fortify/SKILL.md b/.claude/skills/fortify-development/SKILL.md similarity index 99% rename from .claude/skills/developing-with-fortify/SKILL.md rename to .claude/skills/fortify-development/SKILL.md index a09178e3..bd05dd6d 100644 --- a/.claude/skills/developing-with-fortify/SKILL.md +++ b/.claude/skills/fortify-development/SKILL.md @@ -1,5 +1,5 @@ --- -name: developing-with-fortify +name: fortify-development description: Laravel Fortify headless authentication backend development. Activate when implementing authentication features including login, registration, password reset, email verification, two-factor authentication (2FA/TOTP), profile updates, headless auth, authentication scaffolding, or auth guards in Laravel applications. --- diff --git a/.claude/skills/inertia-react-development/SKILL.md b/.claude/skills/inertia-react-development/SKILL.md index 5fe2712d..bb01fd9c 100644 --- a/.claude/skills/inertia-react-development/SKILL.md +++ b/.claude/skills/inertia-react-development/SKILL.md @@ -15,7 +15,7 @@ Activate this skill when: - Creating or modifying React page components for Inertia - Working with forms in React (using `` or `useForm`) - Implementing client-side navigation with `` or `router` -- Using v2 features: deferred props, prefetching, or polling +- Using v2 features: deferred props, prefetching, WhenVisible, InfiniteScroll, once props, flash data, or polling - Building React-specific features with the Inertia protocol ## Documentation @@ -295,14 +295,21 @@ export default function UsersIndex({ users }) { ### Polling -Use the `usePoll` hook to automatically refresh data at intervals. It handles cleanup on unmount and throttles polling when the tab is inactive. +Automatically refresh data at intervals: - + ```react -import { usePoll } from '@inertiajs/react' +import { router } from '@inertiajs/react' +import { useEffect } from 'react' export default function Dashboard({ stats }) { - usePoll(5000) + useEffect(() => { + const interval = setInterval(() => { + router.reload({ only: ['stats'] }) + }, 5000) // Poll every 5 seconds + + return () => clearInterval(interval) + }, []) return (
@@ -313,65 +320,38 @@ export default function Dashboard({ stats }) { } ``` - -```react -import { usePoll } from '@inertiajs/react' +### WhenVisible -export default function Dashboard({ stats }) { - const { start, stop } = usePoll(5000, { - only: ['stats'], - onStart() { - console.log('Polling request started') - }, - onFinish() { - console.log('Polling request finished') - }, - }, { - autoStart: false, - keepAlive: true, - }) +Lazy-load a prop when an element scrolls into view. Useful for deferring expensive data that sits below the fold: - return ( -
-

Dashboard

-
Active Users: {stats.activeUsers}
- - -
- ) -} -``` - -- `autoStart` (default `true`) — set to `false` to start polling manually via the returned `start()` function -- `keepAlive` (default `false`) — set to `true` to prevent throttling when the browser tab is inactive - -### WhenVisible (Infinite Scroll) - -Load more data when user scrolls to a specific element: - - + ```react import { WhenVisible } from '@inertiajs/react' -export default function UsersList({ users }) { +export default function Dashboard({ stats }) { return (
- {users.data.map(user => ( -
{user.name}
- ))} +

Dashboard

- {users.next_page_url && ( - Loading more...
} - /> - )} + {/* stats prop is loaded only when this section scrolls into view */} + Loading stats...
}> + {({ fetching }) => ( +
+

Total Users: {stats.total_users}

+

Revenue: {stats.revenue}

+ {fetching && Refreshing...} +
+ )} + ) } ``` +## Server-Side Patterns + +Server-side patterns (Inertia::render, props, middleware) are covered in inertia-laravel guidelines. + ## Common Pitfalls - Using traditional `
` links instead of Inertia's `` component (breaks SPA behavior) diff --git a/.claude/skills/pennant-development/SKILL.md b/.claude/skills/pennant-development/SKILL.md index 76a8c82f..3b364661 100644 --- a/.claude/skills/pennant-development/SKILL.md +++ b/.claude/skills/pennant-development/SKILL.md @@ -1,6 +1,6 @@ --- name: pennant-development -description: "Manages feature flags with Laravel Pennant. Activates when creating, checking, or toggling feature flags; showing or hiding features conditionally; implementing A/B testing; working with @feature directive; or when the user mentions feature flags, feature toggles, Pennant, conditional features, rollouts, or gradually enabling features." +description: "Use when working with Laravel Pennant the official Laravel feature flag package. Trigger whenever the query mentions Pennant by name or involves feature flags or feature toggles in a Laravel project. Tasks include defining feature flags checking whether features are active creating class based features in `app/Features` using Blade `@feature` directives scoping flags to users or teams building custom Pennant storage drivers protecting routes with feature flags testing feature flags with Pest or PHPUnit and implementing A B testing or gradual rollouts with feature flags. Do not trigger for generic Laravel configuration authorization policies authentication or non Pennant feature management systems." license: MIT metadata: author: laravel diff --git a/.claude/skills/pest-testing/SKILL.md b/.claude/skills/pest-testing/SKILL.md index 56198610..ba774e71 100644 --- a/.claude/skills/pest-testing/SKILL.md +++ b/.claude/skills/pest-testing/SKILL.md @@ -1,6 +1,6 @@ --- name: pest-testing -description: "Tests applications using the Pest 4 PHP framework. Activates when writing tests, creating unit or feature tests, adding assertions, testing Livewire components, browser testing, debugging test failures, working with datasets or mocking; or when the user mentions test, spec, TDD, expects, assertion, coverage, or needs to verify functionality works." +description: "Use this skill for Pest PHP testing in Laravel projects only. Trigger whenever any test is being written, edited, fixed, or refactored — including fixing tests that broke after a code change, adding assertions, converting PHPUnit to Pest, adding datasets, and TDD workflows. Always activate when the user asks how to write something in Pest, mentions test files or directories (tests/Feature, tests/Unit, tests/Browser), or needs browser testing, smoke testing multiple pages for JS errors, or architecture tests. Covers: it()/expect() syntax, datasets, mocking, browser testing (visit/click/fill), smoke testing, arch(), Livewire component tests, RefreshDatabase, and all Pest 4 features. Do not use for factories, seeders, migrations, controllers, models, or non-test PHP code." license: MIT metadata: author: laravel @@ -8,16 +8,6 @@ metadata: # Pest Testing 4 -## When to Apply - -Activate this skill when: - -- Creating new tests (unit, feature, or browser) -- Modifying existing tests -- Debugging test failures -- Working with browser testing or smoke testing -- Writing architecture tests or visual regression tests - ## Documentation Use `search-docs` for detailed Pest 4 patterns and documentation. diff --git a/.claude/skills/tailwindcss-development/SKILL.md b/.claude/skills/tailwindcss-development/SKILL.md index 21a7e463..7c8e295e 100644 --- a/.claude/skills/tailwindcss-development/SKILL.md +++ b/.claude/skills/tailwindcss-development/SKILL.md @@ -1,6 +1,6 @@ --- name: tailwindcss-development -description: "Styles applications using Tailwind CSS v4 utilities. Activates when adding styles, restyling components, working with gradients, spacing, layout, flex, grid, responsive design, dark mode, colors, typography, or borders; or when the user mentions CSS, styling, classes, Tailwind, restyle, hero section, cards, buttons, or any visual/UI changes." +description: "Always invoke when the user's message includes 'tailwind' in any form. Also invoke for: building responsive grid layouts (multi-column card grids, product grids), flex/grid page structures (dashboards with sidebars, fixed topbars, mobile-toggle navs), styling UI components (cards, tables, navbars, pricing sections, forms, inputs, badges), adding dark mode variants, fixing spacing or typography, and Tailwind v3/v4 work. The core use case: writing or fixing Tailwind utility classes in HTML templates (Blade, JSX, Vue). Skip for backend PHP logic, database queries, API routes, JavaScript with no HTML/CSS component, CSS file audits, build tool configuration, and vanilla CSS." license: MIT metadata: author: laravel @@ -8,16 +8,6 @@ metadata: # Tailwind CSS Development -## When to Apply - -Activate this skill when: - -- Adding styles to components or pages -- Working with responsive design -- Implementing dark mode -- Extracting repeated patterns into components -- Debugging spacing or layout issues - ## Documentation Use `search-docs` for detailed Tailwind CSS v4 patterns and documentation. diff --git a/.claude/skills/wayfinder-development/SKILL.md b/.claude/skills/wayfinder-development/SKILL.md index 37d620d7..06ece8fb 100644 --- a/.claude/skills/wayfinder-development/SKILL.md +++ b/.claude/skills/wayfinder-development/SKILL.md @@ -8,13 +8,6 @@ metadata: # Wayfinder Development -## When to Apply - -Activate whenever referencing backend routes in frontend components: -- Importing from `@/actions/` or `@/routes/` -- Calling Laravel routes from TypeScript/JavaScript -- Creating links or navigation to backend endpoints - ## Documentation Use `search-docs` for detailed Wayfinder patterns and documentation. diff --git a/.github/skills/cashier-stripe-development/SKILL.md b/.github/skills/cashier-stripe-development/SKILL.md new file mode 100644 index 00000000..355e8a92 --- /dev/null +++ b/.github/skills/cashier-stripe-development/SKILL.md @@ -0,0 +1,108 @@ +--- +name: cashier-stripe-development +description: "Handles Laravel Cashier Stripe integration including subscriptions, webhooks, Stripe Checkout, invoices, charges, refunds, trials, coupons, metered billing, and payment failure handling. Triggered when a user mentions Cashier, Billable, IncompletePayment, stripe_id, newSubscription, Stripe subscriptions, or billing. Also applies when setting up webhooks, handling SCA/3DS payment failures, testing with Stripe test cards, or troubleshooting incomplete subscriptions, CSRF webhook errors, or migration publish issues." +license: MIT +metadata: + author: laravel +--- + +# Cashier Stripe Development + +## When to Apply + +Activate this skill when: + +- Installing or configuring Laravel Cashier Stripe +- Setting up subscriptions, trials, quantities, or plan swapping +- Handling webhooks or SCA/3DS payment failures +- Working with Stripe Checkout, invoices, or charges +- Testing billing scenarios with Stripe test cards or tokens + +## Documentation + +Use `search-docs` for detailed Cashier patterns and documentation covering subscriptions, webhooks, Stripe Checkout, invoices, payment methods, and testing. + +For deeper guidance on specific topics, read the relevant reference file before implementing: + +- `references/subscriptions.md` covers subscription creation, status checks, swapping, trials, quantities, and multiple products +- `references/webhooks.md` covers webhook setup, custom handlers, CSRF exclusion, and local development with the Stripe CLI +- `references/testing.md` covers Stripe test cards, payment method tokens, and feature test patterns + +## Basic Usage + +### Installation + +```bash +php artisan vendor:publish --tag="cashier-migrations" +php artisan migrate +php artisan vendor:publish --tag="cashier-config" +``` + +### Environment Variables + +``` +STRIPE_KEY=pk_test_... +STRIPE_SECRET=sk_test_... +STRIPE_WEBHOOK_SECRET=whsec_... +CASHIER_CURRENCY=usd +CASHIER_CURRENCY_LOCALE=en_US +``` + +### Billable Model + + +```php +use Laravel\Cashier\Billable; + +class User extends Authenticatable +{ + use Billable; +} +``` + +For a non-User model, register it in a service provider: + + +```php +// In AppServiceProvider::boot() +Cashier::useCustomerModel(Team::class); +``` + +### Creating a Subscription + + +```php +use Laravel\Cashier\Exceptions\IncompletePayment; + +try { + $user->newSubscription('default', 'price_xxxx')->create($paymentMethodId); +} catch (IncompletePayment $e) { + return redirect()->route('cashier.payment', [$e->payment->id, 'redirect' => route('home')]); +} +``` + +Always wrap subscription creation in a try/catch for `IncompletePayment`. When a card requires 3DS authentication, Cashier throws this exception. The `cashier.payment` route is auto-registered and handles the confirmation flow. + +## Verification + +1. Run migrations and confirm `stripe_id`, `pm_type`, `pm_last_four`, and `trial_ends_at` columns exist on the billable model table +2. Test the webhook endpoint with `stripe listen --forward-to localhost/stripe/webhook` if you use the default path, or swap `stripe` for your configured `CASHIER_PATH` +3. Confirm `$user->subscribed('default')` returns the expected value for active and incomplete subscriptions + +## Common Pitfalls + +- The migration publish tag is `cashier-migrations`, not `cashier`. Running `migrate` before publishing results in missing columns and tables. +- `CASHIER_CURRENCY` must be set explicitly. It defaults to USD, which silently breaks non-US apps. +- The Stripe CLI generates its own webhook signing secret. It is different from the Dashboard endpoint secret. Using the wrong one causes signature verification failures. +- The webhook route must be excluded from CSRF verification using your configured `cashier.path`. If you change `CASHIER_PATH` from `stripe` to `billing`, exclude `billing/*`, not `stripe/*`. +- `canceled()` returns true as soon as `cancel()` is called, but the user still has access during the grace period. Use `ended()` to confirm access is fully revoked. +- `subscribed()` returns true during the grace period even though the subscription is canceled. +- `subscribed()` returns false for `incomplete` and `past_due` subscriptions by default. +- Prices cannot be swapped and quantity cannot be updated while a subscription has an incomplete payment. +- When extending `WebhookController`, call `Cashier::ignoreRoutes()` in a service provider and re-register both `cashier.payment` and `cashier.webhook` under the configured `cashier.path`. +- Use `Cashier::useCustomerModel()` in a service provider to set a custom billable model. There is no `CASHIER_MODEL` env var. +- `trial_ends_at` is a local database column synced via webhooks. It will be stale if webhooks are not configured in production. +- In MySQL, the `stripe_id` column must use `utf8_bin` collation to avoid case-sensitivity issues. +- `noProrate()` has no effect when combined with `swapAndInvoice()`. That method always prorates. +- Methods like `withPromotionCode()` require the Stripe API ID such as `promo_xxxx`, not the customer-facing code. Use `findPromotionCode()` to resolve a code to its ID. +- Always use `search-docs` for the latest Cashier documentation rather than relying on this skill alone. \ No newline at end of file diff --git a/.github/skills/cashier-stripe-development/references/subscriptions.md b/.github/skills/cashier-stripe-development/references/subscriptions.md new file mode 100644 index 00000000..ad7113ea --- /dev/null +++ b/.github/skills/cashier-stripe-development/references/subscriptions.md @@ -0,0 +1,108 @@ +# Subscriptions Reference + +Use `search-docs` for authoritative documentation on subscriptions. + +## Status Checks + +| Method | Returns true when | +|---|---| +| `$user->subscribed('default')` | Active or on grace period | +| `->onTrial()` | Trial period active | +| `->onGracePeriod()` | Canceled, period not yet ended | +| `->canceled()` | `ends_at` is set, may still have access | +| `->ended()` | Canceled and grace period expired | +| `->incomplete()` | Awaiting SCA/3DS confirmation | +| `->pastDue()` | Payment overdue | +| `->recurring()` | Active and not on trial | + +Check by product or price: + +```php +$user->subscribedToProduct('prod_premium', 'default'); +$user->subscribedToPrice('price_monthly', 'default'); +``` + +## Swapping Plans + +```php +$user->subscription('default')->swap('price_new'); +$user->subscription('default')->noProrate()->swap('price_new'); +$user->subscription('default')->swapAndInvoice('price_new'); +$user->subscription('default')->skipTrial()->swap('price_new'); +``` + +## Quantity + +```php +$user->subscription('default')->incrementQuantity(); +$user->subscription('default')->decrementQuantity(); +$user->subscription('default')->updateQuantity(10); +$user->subscription('default')->noProrate()->updateQuantity(10); +``` + +## Trials + +```php +$user->newSubscription('default', 'price_xxxx') + ->trialDays(14) + ->create($paymentMethodId); + +$subscription->extendTrial(now()->addDays(7)); +``` + +## Multiple Products on One Subscription + +```php +$user->newSubscription('default', ['price_monthly', 'price_chat']) + ->quantity(5, 'price_chat') + ->create($paymentMethod); + +$user->subscription('default')->addPrice('price_chat'); +$user->subscription('default')->removePrice('price_chat'); +$user->subscription('default')->swap(['price_pro', 'price_chat']); +``` + +## Multiple Subscriptions + +```php +$user->newSubscription('swimming', 'price_swimming_monthly')->create($pm); +$user->newSubscription('gym', 'price_gym_monthly')->create($pm); + +$user->subscription('swimming')->swap('price_swimming_yearly'); +$user->subscription('gym')->cancel(); +``` + +## Cancellation and Resumption + +```php +$user->subscription('default')->cancel(); // At end of billing period +$user->subscription('default')->cancelNow(); // Immediately +$user->subscription('default')->resume(); // During grace period only +``` + +## Incomplete Payment Handling + +```php +if ($user->hasIncompletePayment('default')) { + $paymentId = $user->subscription('default')->latestPayment()->id; + return redirect()->route('cashier.payment', $paymentId); +} +``` + +Opt out of default deactivation behavior: + +```php +Cashier::keepPastDueSubscriptionsActive(); +Cashier::keepIncompleteSubscriptionsActive(); +``` + +## Metered / Usage-Based Billing + +```php +$user->newSubscription('default') + ->meteredPrice('price_metered') + ->create($paymentMethodId); + +$user->reportMeterEvent('emails-sent'); +$user->reportMeterEvent('emails-sent', quantity: 15); +``` \ No newline at end of file diff --git a/.github/skills/cashier-stripe-development/references/testing.md b/.github/skills/cashier-stripe-development/references/testing.md new file mode 100644 index 00000000..97e91f91 --- /dev/null +++ b/.github/skills/cashier-stripe-development/references/testing.md @@ -0,0 +1,52 @@ +# Testing Reference + +Use `search-docs` for authoritative documentation on testing Cashier integrations. + +## Test Cards and Tokens + +Use card numbers for browser-based flows (Stripe.js / Checkout). Use `pm_card_*` tokens directly in feature tests that call the Stripe API. + +| Card Number | Token | Behavior | +|---|---|---| +| `4242 4242 4242 4242` | `pm_card_visa` | Succeeds immediately | +| `4000 0025 0000 3155` | `pm_card_threeDSecure2Required` | Requires SCA/3DS | +| `4000 0027 6000 3184` | `pm_card_authenticationRequired` | Requires authentication | +| `4000 0000 0000 9995` | `pm_card_chargeDeclinedInsufficientFunds` | Declined, insufficient funds | +| `4000 0000 0000 0002` | `pm_card_chargeDeclined` | Declined | + +Use expiry `12/34`, any CVC, any ZIP for card number inputs. + +## Feature Test Example + +Feature tests that hit the real Stripe test API use `pm_card_*` tokens: + +```php +public function test_user_can_subscribe(): void +{ + $user = User::factory()->create(); + + $user->newSubscription('default', 'price_xxxx') + ->create('pm_card_visa'); + + $this->assertTrue($user->subscribed('default')); +} + +public function test_incomplete_payment_is_handled(): void +{ + $user = User::factory()->create(); + + try { + $user->newSubscription('default', 'price_xxxx') + ->create('pm_card_threeDSecure2Required'); + } catch (\Laravel\Cashier\Exceptions\IncompletePayment $e) { + $this->assertTrue($user->subscription('default')->incomplete()); + } +} +``` + +## Setup Notes + +- Use Stripe test mode keys (`sk_test_...`, `pk_test_...`) in your test environment +- Cashier does not ship a global `fake()` helper. Tests hit the real Stripe test API by default. +- Refer to `tests/Feature/` in the Cashier package itself for integration test patterns covering subscription creation, payment methods, and webhook handling +- Use `search-docs` for current guidance on mocking Stripe HTTP calls or using Stripe's test clock feature for time-sensitive scenarios \ No newline at end of file diff --git a/.github/skills/cashier-stripe-development/references/webhooks.md b/.github/skills/cashier-stripe-development/references/webhooks.md new file mode 100644 index 00000000..19d2f708 --- /dev/null +++ b/.github/skills/cashier-stripe-development/references/webhooks.md @@ -0,0 +1,132 @@ +# Webhooks Reference + +Use `search-docs` for authoritative documentation on webhooks. + +## Auto-Registered Routes + +Cashier registers two routes automatically under the `cashier.path` prefix (`config('cashier.path')`, default `stripe`): + +- `POST /{cashier.path}/webhook` named `cashier.webhook` +- `GET /{cashier.path}/payment/{id}` named `cashier.payment` + +With the default config these are `/stripe/webhook` and `/stripe/payment/{id}`. If you set `CASHIER_PATH=billing`, they become `/billing/webhook` and `/billing/payment/{id}`. + +## CSRF Exclusion + +Use the same path prefix you configured for Cashier here. If `CASHIER_PATH=billing`, exclude `billing/*` instead of `stripe/*`. + +**Laravel 11+ (`bootstrap/app.php`, default path example):** + +```php +->withMiddleware(function (Middleware $middleware) { + $middleware->validateCsrfTokens(except: ['stripe/*']); +}) +``` + +**Laravel 10 (`app/Http/Middleware/VerifyCsrfToken.php`, default path example):** + +```php +protected $except = [ + 'stripe/*', +]; +``` + +## Local Development with Stripe CLI + +If you changed `cashier.path`, forward Stripe CLI events to that URL instead of `/stripe/webhook`. + +```bash +stripe login +stripe listen --forward-to your-app.test/stripe/webhook +stripe trigger invoice.payment_succeeded +``` + +The CLI outputs a `whsec_...` signing secret specific to that session. Set it as `STRIPE_WEBHOOK_SECRET` locally. It is not the same as the Dashboard endpoint secret. + +## Registering Events in the Stripe Dashboard + +Use the Artisan command to create the endpoint automatically with all required events: + +```bash +php artisan cashier:webhook +``` + +Cashier's `cashier:webhook` command registers these events by default: + +- `customer.subscription.created` +- `customer.subscription.updated` +- `customer.subscription.deleted` +- `customer.updated` / `customer.deleted` +- `invoice.payment_action_required` +- `invoice.payment_succeeded` +- `payment_method.automatically_updated` + +Cashier's `WebhookController` has built-in handlers for all of the above except `invoice.payment_succeeded`. For renewal hooks, prefer `WebhookReceived` / `WebhookHandled` listeners unless you intentionally add your own controller method. + +## Custom Handlers: Extending WebhookController + +Method name pattern: `handle` + StudlyCase of event type with dots replaced by underscores. + +`customer.subscription.created` becomes `handleCustomerSubscriptionCreated`. + +```php +use Laravel\Cashier\Http\Controllers\WebhookController as CashierController; + +class StripeWebhookController extends CashierController +{ + public function handleCustomerSubscriptionCreated(array $payload) + { + $response = parent::handleCustomerSubscriptionCreated($payload); + + // your logic after Cashier syncs the subscription + + return $response; + } +} +``` + +If you add a method for an event Cashier does not handle internally, such as `invoice.payment_succeeded`, do not call `parent::handle...()` unless the base controller actually defines that method. + +In a service provider, disable auto-registration and re-register both Cashier routes so the incomplete-payment flow and `cashier:webhook` command keep working: + +```php +Cashier::ignoreRoutes(); +``` + +```php +// routes/web.php +use App\Http\Controllers\StripeWebhookController; +use Illuminate\Support\Facades\Route; +use Laravel\Cashier\Http\Controllers\PaymentController; + +Route::prefix(config('cashier.path')) + ->name('cashier.') + ->group(function () { + Route::get('payment/{id}', [PaymentController::class, 'show'])->name('payment'); + Route::post('webhook', [StripeWebhookController::class, 'handleWebhook'])->name('webhook'); + }); +``` + +Keep the `cashier.webhook` route name unless you plan to pass `--url` explicitly to `php artisan cashier:webhook`. + +## Custom Handlers: Listening to Events + +The simpler option when you do not need to replace Cashier's internal logic, or when you want to react to events such as `invoice.payment_succeeded` that Cashier does not process itself: + +```php +use Laravel\Cashier\Events\WebhookReceived; +use Laravel\Cashier\Events\WebhookHandled; + +// WebhookReceived fires for every event before Cashier processes it +// WebhookHandled fires after Cashier processes it + +Event::listen(WebhookReceived::class, function (WebhookReceived $event) { + if ($event->payload['type'] === 'invoice.payment_succeeded') { + // handle renewal + } +}); +``` + +## Signature Verification + +`VerifyWebhookSignature` middleware is applied automatically when `cashier.webhook.secret` is set. No extra wiring is needed. \ No newline at end of file diff --git a/.github/skills/developing-with-fortify/SKILL.md b/.github/skills/fortify-development/SKILL.md similarity index 99% rename from .github/skills/developing-with-fortify/SKILL.md rename to .github/skills/fortify-development/SKILL.md index a09178e3..bd05dd6d 100644 --- a/.github/skills/developing-with-fortify/SKILL.md +++ b/.github/skills/fortify-development/SKILL.md @@ -1,5 +1,5 @@ --- -name: developing-with-fortify +name: fortify-development description: Laravel Fortify headless authentication backend development. Activate when implementing authentication features including login, registration, password reset, email verification, two-factor authentication (2FA/TOTP), profile updates, headless auth, authentication scaffolding, or auth guards in Laravel applications. --- diff --git a/.github/skills/inertia-react-development/SKILL.md b/.github/skills/inertia-react-development/SKILL.md index 5fe2712d..bb01fd9c 100644 --- a/.github/skills/inertia-react-development/SKILL.md +++ b/.github/skills/inertia-react-development/SKILL.md @@ -15,7 +15,7 @@ Activate this skill when: - Creating or modifying React page components for Inertia - Working with forms in React (using `` or `useForm`) - Implementing client-side navigation with `` or `router` -- Using v2 features: deferred props, prefetching, or polling +- Using v2 features: deferred props, prefetching, WhenVisible, InfiniteScroll, once props, flash data, or polling - Building React-specific features with the Inertia protocol ## Documentation @@ -295,14 +295,21 @@ export default function UsersIndex({ users }) { ### Polling -Use the `usePoll` hook to automatically refresh data at intervals. It handles cleanup on unmount and throttles polling when the tab is inactive. +Automatically refresh data at intervals: - + ```react -import { usePoll } from '@inertiajs/react' +import { router } from '@inertiajs/react' +import { useEffect } from 'react' export default function Dashboard({ stats }) { - usePoll(5000) + useEffect(() => { + const interval = setInterval(() => { + router.reload({ only: ['stats'] }) + }, 5000) // Poll every 5 seconds + + return () => clearInterval(interval) + }, []) return (
@@ -313,65 +320,38 @@ export default function Dashboard({ stats }) { } ``` - -```react -import { usePoll } from '@inertiajs/react' +### WhenVisible -export default function Dashboard({ stats }) { - const { start, stop } = usePoll(5000, { - only: ['stats'], - onStart() { - console.log('Polling request started') - }, - onFinish() { - console.log('Polling request finished') - }, - }, { - autoStart: false, - keepAlive: true, - }) +Lazy-load a prop when an element scrolls into view. Useful for deferring expensive data that sits below the fold: - return ( -
-

Dashboard

-
Active Users: {stats.activeUsers}
- - -
- ) -} -``` - -- `autoStart` (default `true`) — set to `false` to start polling manually via the returned `start()` function -- `keepAlive` (default `false`) — set to `true` to prevent throttling when the browser tab is inactive - -### WhenVisible (Infinite Scroll) - -Load more data when user scrolls to a specific element: - - + ```react import { WhenVisible } from '@inertiajs/react' -export default function UsersList({ users }) { +export default function Dashboard({ stats }) { return (
- {users.data.map(user => ( -
{user.name}
- ))} +

Dashboard

- {users.next_page_url && ( - Loading more...
} - /> - )} + {/* stats prop is loaded only when this section scrolls into view */} + Loading stats...
}> + {({ fetching }) => ( +
+

Total Users: {stats.total_users}

+

Revenue: {stats.revenue}

+ {fetching && Refreshing...} +
+ )} + ) } ``` +## Server-Side Patterns + +Server-side patterns (Inertia::render, props, middleware) are covered in inertia-laravel guidelines. + ## Common Pitfalls - Using traditional `
` links instead of Inertia's `` component (breaks SPA behavior) diff --git a/.github/skills/pennant-development/SKILL.md b/.github/skills/pennant-development/SKILL.md index 76a8c82f..3b364661 100644 --- a/.github/skills/pennant-development/SKILL.md +++ b/.github/skills/pennant-development/SKILL.md @@ -1,6 +1,6 @@ --- name: pennant-development -description: "Manages feature flags with Laravel Pennant. Activates when creating, checking, or toggling feature flags; showing or hiding features conditionally; implementing A/B testing; working with @feature directive; or when the user mentions feature flags, feature toggles, Pennant, conditional features, rollouts, or gradually enabling features." +description: "Use when working with Laravel Pennant the official Laravel feature flag package. Trigger whenever the query mentions Pennant by name or involves feature flags or feature toggles in a Laravel project. Tasks include defining feature flags checking whether features are active creating class based features in `app/Features` using Blade `@feature` directives scoping flags to users or teams building custom Pennant storage drivers protecting routes with feature flags testing feature flags with Pest or PHPUnit and implementing A B testing or gradual rollouts with feature flags. Do not trigger for generic Laravel configuration authorization policies authentication or non Pennant feature management systems." license: MIT metadata: author: laravel diff --git a/.github/skills/pest-testing/SKILL.md b/.github/skills/pest-testing/SKILL.md index 56198610..ba774e71 100644 --- a/.github/skills/pest-testing/SKILL.md +++ b/.github/skills/pest-testing/SKILL.md @@ -1,6 +1,6 @@ --- name: pest-testing -description: "Tests applications using the Pest 4 PHP framework. Activates when writing tests, creating unit or feature tests, adding assertions, testing Livewire components, browser testing, debugging test failures, working with datasets or mocking; or when the user mentions test, spec, TDD, expects, assertion, coverage, or needs to verify functionality works." +description: "Use this skill for Pest PHP testing in Laravel projects only. Trigger whenever any test is being written, edited, fixed, or refactored — including fixing tests that broke after a code change, adding assertions, converting PHPUnit to Pest, adding datasets, and TDD workflows. Always activate when the user asks how to write something in Pest, mentions test files or directories (tests/Feature, tests/Unit, tests/Browser), or needs browser testing, smoke testing multiple pages for JS errors, or architecture tests. Covers: it()/expect() syntax, datasets, mocking, browser testing (visit/click/fill), smoke testing, arch(), Livewire component tests, RefreshDatabase, and all Pest 4 features. Do not use for factories, seeders, migrations, controllers, models, or non-test PHP code." license: MIT metadata: author: laravel @@ -8,16 +8,6 @@ metadata: # Pest Testing 4 -## When to Apply - -Activate this skill when: - -- Creating new tests (unit, feature, or browser) -- Modifying existing tests -- Debugging test failures -- Working with browser testing or smoke testing -- Writing architecture tests or visual regression tests - ## Documentation Use `search-docs` for detailed Pest 4 patterns and documentation. diff --git a/.github/skills/tailwindcss-development/SKILL.md b/.github/skills/tailwindcss-development/SKILL.md index 21a7e463..7c8e295e 100644 --- a/.github/skills/tailwindcss-development/SKILL.md +++ b/.github/skills/tailwindcss-development/SKILL.md @@ -1,6 +1,6 @@ --- name: tailwindcss-development -description: "Styles applications using Tailwind CSS v4 utilities. Activates when adding styles, restyling components, working with gradients, spacing, layout, flex, grid, responsive design, dark mode, colors, typography, or borders; or when the user mentions CSS, styling, classes, Tailwind, restyle, hero section, cards, buttons, or any visual/UI changes." +description: "Always invoke when the user's message includes 'tailwind' in any form. Also invoke for: building responsive grid layouts (multi-column card grids, product grids), flex/grid page structures (dashboards with sidebars, fixed topbars, mobile-toggle navs), styling UI components (cards, tables, navbars, pricing sections, forms, inputs, badges), adding dark mode variants, fixing spacing or typography, and Tailwind v3/v4 work. The core use case: writing or fixing Tailwind utility classes in HTML templates (Blade, JSX, Vue). Skip for backend PHP logic, database queries, API routes, JavaScript with no HTML/CSS component, CSS file audits, build tool configuration, and vanilla CSS." license: MIT metadata: author: laravel @@ -8,16 +8,6 @@ metadata: # Tailwind CSS Development -## When to Apply - -Activate this skill when: - -- Adding styles to components or pages -- Working with responsive design -- Implementing dark mode -- Extracting repeated patterns into components -- Debugging spacing or layout issues - ## Documentation Use `search-docs` for detailed Tailwind CSS v4 patterns and documentation. diff --git a/.github/skills/wayfinder-development/SKILL.md b/.github/skills/wayfinder-development/SKILL.md index 37d620d7..06ece8fb 100644 --- a/.github/skills/wayfinder-development/SKILL.md +++ b/.github/skills/wayfinder-development/SKILL.md @@ -8,13 +8,6 @@ metadata: # Wayfinder Development -## When to Apply - -Activate whenever referencing backend routes in frontend components: -- Importing from `@/actions/` or `@/routes/` -- Calling Laravel routes from TypeScript/JavaScript -- Creating links or navigation to backend endpoints - ## Documentation Use `search-docs` for detailed Wayfinder patterns and documentation. diff --git a/AGENTS.md b/AGENTS.md index dc9981c8..17feacb5 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -18,14 +18,15 @@ The Laravel Boost guidelines are specifically curated by Laravel maintainers for This application is a Laravel application and its main Laravel ecosystems package & versions are below. You are an expert with them all. Ensure you abide by these specific packages & versions. -- php - 8.4.17 +- php - 8.4 - inertiajs/inertia-laravel (INERTIA_LARAVEL) - v2 - laravel/cashier (CASHIER) - v16 - laravel/fortify (FORTIFY) - v1 -- laravel/framework (LARAVEL) - v12 +- laravel/framework (LARAVEL) - v13 - laravel/pennant (PENNANT) - v1 - laravel/prompts (PROMPTS) - v0 - laravel/wayfinder (WAYFINDER) - v0 +- larastan/larastan (LARASTAN) - v3 - laravel/boost (BOOST) - v2 - laravel/mcp (MCP) - v0 - laravel/pail (PAIL) - v1 @@ -44,12 +45,13 @@ This application is a Laravel application and its main Laravel ecosystems packag This project has domain-specific skills available. You MUST activate the relevant skill whenever you work in that domain—don't wait until you're stuck. -- `pennant-development` — Manages feature flags with Laravel Pennant. Activates when creating, checking, or toggling feature flags; showing or hiding features conditionally; implementing A/B testing; working with @feature directive; or when the user mentions feature flags, feature toggles, Pennant, conditional features, rollouts, or gradually enabling features. +- `cashier-stripe-development` — Handles Laravel Cashier Stripe integration including subscriptions, webhooks, Stripe Checkout, invoices, charges, refunds, trials, coupons, metered billing, and payment failure handling. Triggered when a user mentions Cashier, Billable, IncompletePayment, stripe_id, newSubscription, Stripe subscriptions, or billing. Also applies when setting up webhooks, handling SCA/3DS payment failures, testing with Stripe test cards, or troubleshooting incomplete subscriptions, CSRF webhook errors, or migration publish issues. +- `pennant-development` — Use when working with Laravel Pennant the official Laravel feature flag package. Trigger whenever the query mentions Pennant by name or involves feature flags or feature toggles in a Laravel project. Tasks include defining feature flags checking whether features are active creating class based features in `app/Features` using Blade `@feature` directives scoping flags to users or teams building custom Pennant storage drivers protecting routes with feature flags testing feature flags with Pest or PHPUnit and implementing A B testing or gradual rollouts with feature flags. Do not trigger for generic Laravel configuration authorization policies authentication or non Pennant feature management systems. - `wayfinder-development` — Activates whenever referencing backend routes in frontend components. Use when importing from @/actions or @/routes, calling Laravel routes from TypeScript, or working with Wayfinder route functions. -- `pest-testing` — Tests applications using the Pest 4 PHP framework. Activates when writing tests, creating unit or feature tests, adding assertions, testing Livewire components, browser testing, debugging test failures, working with datasets or mocking; or when the user mentions test, spec, TDD, expects, assertion, coverage, or needs to verify functionality works. -- `inertia-react-development` — Develops Inertia.js v2 React client-side applications. Activates when creating React pages, forms, or navigation; using <Link>, <Form>, useForm, or router; working with deferred props, prefetching, or polling; or when user mentions React with Inertia, React pages, React forms, or React navigation. -- `tailwindcss-development` — Styles applications using Tailwind CSS v4 utilities. Activates when adding styles, restyling components, working with gradients, spacing, layout, flex, grid, responsive design, dark mode, colors, typography, or borders; or when the user mentions CSS, styling, classes, Tailwind, restyle, hero section, cards, buttons, or any visual/UI changes. -- `developing-with-fortify` — Laravel Fortify headless authentication backend development. Activate when implementing authentication features including login, registration, password reset, email verification, two-factor authentication (2FA/TOTP), profile updates, headless auth, authentication scaffolding, or auth guards in Laravel applications. +- `pest-testing` — Use this skill for Pest PHP testing in Laravel projects only. Trigger whenever any test is being written, edited, fixed, or refactored — including fixing tests that broke after a code change, adding assertions, converting PHPUnit to Pest, adding datasets, and TDD workflows. Always activate when the user asks how to write something in Pest, mentions test files or directories (tests/Feature, tests/Unit, tests/Browser), or needs browser testing, smoke testing multiple pages for JS errors, or architecture tests. Covers: it()/expect() syntax, datasets, mocking, browser testing (visit/click/fill), smoke testing, arch(), Livewire component tests, RefreshDatabase, and all Pest 4 features. Do not use for factories, seeders, migrations, controllers, models, or non-test PHP code. +- `inertia-react-development` — Develops Inertia.js v2 React client-side applications. Activates when creating React pages, forms, or navigation; using , , useForm, or router; working with deferred props, prefetching, or polling; or when user mentions React with Inertia, React pages, React forms, or React navigation. +- `tailwindcss-development` — Always invoke when the user's message includes 'tailwind' in any form. Also invoke for: building responsive grid layouts (multi-column card grids, product grids), flex/grid page structures (dashboards with sidebars, fixed topbars, mobile-toggle navs), styling UI components (cards, tables, navbars, pricing sections, forms, inputs, badges), adding dark mode variants, fixing spacing or typography, and Tailwind v3/v4 work. The core use case: writing or fixing Tailwind utility classes in HTML templates (Blade, JSX, Vue). Skip for backend PHP logic, database queries, API routes, JavaScript with no HTML/CSS component, CSS file audits, build tool configuration, and vanilla CSS. +- `fortify-development` — Laravel Fortify headless authentication backend development. Activate when implementing authentication features including login, registration, password reset, email verification, two-factor authentication (2FA/TOTP), profile updates, headless auth, authentication scaffolding, or auth guards in Laravel applications. ## Conventions @@ -84,19 +86,23 @@ This project has domain-specific skills available. You MUST activate the relevan - Laravel Boost is an MCP server that comes with powerful tools designed specifically for this application. Use them. -## Artisan +## Artisan Commands -- Use the `list-artisan-commands` tool when you need to call an Artisan command to double-check the available parameters. +- Run Artisan commands directly via the command line (e.g., `php artisan route:list`, `php artisan tinker --execute "..."`). +- Use `php artisan list` to discover available commands and `php artisan [command] --help` to check parameters. ## URLs - Whenever you share a project URL with the user, you should use the `get-absolute-url` tool to ensure you're using the correct scheme, domain/IP, and port. -## Tinker / Debugging +## Debugging -- You should use the `tinker` tool when you need to execute PHP to debug code or query Eloquent models directly. - Use the `database-query` tool when you only need to read from the database. - Use the `database-schema` tool to inspect table structure before writing migrations or models. +- To execute PHP code for debugging, run `php artisan tinker --execute "your code here"` directly. +- To read configuration values, read the config files directly or run `php artisan config:show [key]`. +- To inspect routes, run `php artisan route:list` directly. +- To check environment variables, read the `.env` file directly. ## Reading Browser Logs With the `browser-logs` Tool @@ -127,7 +133,7 @@ This project has domain-specific skills available. You MUST activate the relevan ## Constructors - Use PHP 8 constructor property promotion in `__construct()`. - - `public function __construct(public GitHub $github) { }` + - `public function __construct(public GitHub $github) { }` - Do not allow empty `__construct()` methods with zero parameters unless the constructor is private. ## Type Declarations @@ -136,7 +142,6 @@ This project has domain-specific skills available. You MUST activate the relevan - Use appropriate PHP type hints for method parameters. - ```php protected function isAccessible(User $user, ?string $path = null): bool { @@ -163,7 +168,7 @@ protected function isAccessible(User $user, ?string $path = null): bool - 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. -=== inertia-laravel/v2 rules === +=== inertia-laravel/core rules === # Inertia @@ -175,14 +180,14 @@ protected function isAccessible(User $user, ?string $path = null): bool # Inertia v2 - Use all Inertia features from v1 and v2. Check the documentation before making changes to ensure the correct approach. -- New features: deferred props, infinite scrolling (merging props + `WhenVisible`), lazy loading on scroll, polling, prefetching. +- New features: deferred props, infinite scroll, merging props, polling, prefetching, once props, flash data. - When using deferred props, add an empty state with a pulsing or animated skeleton. === 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 the `list-artisan-commands` tool. +- 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. @@ -196,7 +201,7 @@ protected function isAccessible(User $user, ?string $path = null): bool ### Model Creation -- When creating new models, create useful factories and seeders for them too. Ask the user if they need any other things, using `list-artisan-commands` to check the available options to `php artisan make:model`. +- 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 @@ -233,39 +238,6 @@ protected function isAccessible(User $user, ?string $path = null): bool - 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`. -=== laravel/v12 rules === - -# Laravel 12 - -- CRITICAL: ALWAYS use `search-docs` tool for version-specific Laravel documentation and updated code examples. -- Since Laravel 11, Laravel has a new streamlined file structure which this project uses. - -## Laravel 12 Structure - -- In Laravel 12, middleware are no longer registered in `app/Http/Kernel.php`. -- Middleware are configured declaratively in `bootstrap/app.php` using `Application::configure()->withMiddleware()`. -- `bootstrap/app.php` is the file to register middleware, exceptions, and routing files. -- `bootstrap/providers.php` contains application specific service providers. -- The `app\Console\Kernel.php` file no longer exists; use `bootstrap/app.php` or `routes/console.php` for console configuration. -- Console commands in `app/Console/Commands/` are automatically available and do not require manual registration. - -## Database - -- When modifying a column, the migration must include all of the attributes that were previously defined on the column. Otherwise, they will be dropped and lost. -- Laravel 12 allows limiting eagerly loaded records natively, without external packages: `$query->latest()->limit(10);`. - -### Models - -- Casts can and likely should be set in a `casts()` method on a model rather than the `$casts` property. Follow existing conventions from other models. - -=== pennant/core rules === - -# Laravel Pennant - -- This application uses Laravel Pennant for feature flag management, providing a flexible system for controlling feature availability across different organizations and user types. -- IMPORTANT: Always use `search-docs` tool for version-specific Pennant documentation and updated code examples. -- IMPORTANT: Activate `pennant-development` every time you're working with a Pennant or feature-flag-related task. - === wayfinder/core rules === # Laravel Wayfinder @@ -292,8 +264,6 @@ Wayfinder generates TypeScript functions for Laravel routes. Import from `@/acti - This project uses Pest for testing. Create tests: `php artisan make:test --pest {name}`. - Run tests: `php artisan test --compact` or filter: `php artisan test --compact --filter=testName`. - Do NOT delete tests without approval. -- CRITICAL: ALWAYS use `search-docs` tool for version-specific Pest documentation and updated code examples. -- IMPORTANT: Activate `pest-testing` every time you're working with a Pest or testing-related task. === inertia-react/core rules === @@ -301,14 +271,6 @@ Wayfinder generates TypeScript functions for Laravel routes. Import from `@/acti - IMPORTANT: Activate `inertia-react-development` when working with Inertia React client-side patterns. -=== tailwindcss/core rules === - -# Tailwind CSS - -- Always use existing Tailwind conventions; check project patterns before adding new ones. -- IMPORTANT: Always use `search-docs` tool for version-specific Tailwind CSS documentation and updated code examples. Never rely on training data. -- IMPORTANT: Activate `tailwindcss-development` every time you're working with a Tailwind CSS or styling-related task. - === laravel/fortify rules === # Laravel Fortify diff --git a/CLAUDE.md b/CLAUDE.md index 55656f7e..1bf32793 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -105,14 +105,15 @@ The Laravel Boost guidelines are specifically curated by Laravel maintainers for This application is a Laravel application and its main Laravel ecosystems package & versions are below. You are an expert with them all. Ensure you abide by these specific packages & versions. -- php - 8.4.17 +- php - 8.4 - inertiajs/inertia-laravel (INERTIA_LARAVEL) - v2 - laravel/cashier (CASHIER) - v16 - laravel/fortify (FORTIFY) - v1 -- laravel/framework (LARAVEL) - v12 +- laravel/framework (LARAVEL) - v13 - laravel/pennant (PENNANT) - v1 - laravel/prompts (PROMPTS) - v0 - laravel/wayfinder (WAYFINDER) - v0 +- larastan/larastan (LARASTAN) - v3 - laravel/boost (BOOST) - v2 - laravel/mcp (MCP) - v0 - laravel/pail (PAIL) - v1 @@ -131,12 +132,13 @@ This application is a Laravel application and its main Laravel ecosystems packag This project has domain-specific skills available. You MUST activate the relevant skill whenever you work in that domain—don't wait until you're stuck. -- `pennant-development` — Manages feature flags with Laravel Pennant. Activates when creating, checking, or toggling feature flags; showing or hiding features conditionally; implementing A/B testing; working with @feature directive; or when the user mentions feature flags, feature toggles, Pennant, conditional features, rollouts, or gradually enabling features. +- `cashier-stripe-development` — Handles Laravel Cashier Stripe integration including subscriptions, webhooks, Stripe Checkout, invoices, charges, refunds, trials, coupons, metered billing, and payment failure handling. Triggered when a user mentions Cashier, Billable, IncompletePayment, stripe_id, newSubscription, Stripe subscriptions, or billing. Also applies when setting up webhooks, handling SCA/3DS payment failures, testing with Stripe test cards, or troubleshooting incomplete subscriptions, CSRF webhook errors, or migration publish issues. +- `pennant-development` — Use when working with Laravel Pennant the official Laravel feature flag package. Trigger whenever the query mentions Pennant by name or involves feature flags or feature toggles in a Laravel project. Tasks include defining feature flags checking whether features are active creating class based features in `app/Features` using Blade `@feature` directives scoping flags to users or teams building custom Pennant storage drivers protecting routes with feature flags testing feature flags with Pest or PHPUnit and implementing A B testing or gradual rollouts with feature flags. Do not trigger for generic Laravel configuration authorization policies authentication or non Pennant feature management systems. - `wayfinder-development` — Activates whenever referencing backend routes in frontend components. Use when importing from @/actions or @/routes, calling Laravel routes from TypeScript, or working with Wayfinder route functions. -- `pest-testing` — Tests applications using the Pest 4 PHP framework. Activates when writing tests, creating unit or feature tests, adding assertions, testing Livewire components, browser testing, debugging test failures, working with datasets or mocking; or when the user mentions test, spec, TDD, expects, assertion, coverage, or needs to verify functionality works. -- `inertia-react-development` — Develops Inertia.js v2 React client-side applications. Activates when creating React pages, forms, or navigation; using <Link>, <Form>, useForm, or router; working with deferred props, prefetching, or polling; or when user mentions React with Inertia, React pages, React forms, or React navigation. -- `tailwindcss-development` — Styles applications using Tailwind CSS v4 utilities. Activates when adding styles, restyling components, working with gradients, spacing, layout, flex, grid, responsive design, dark mode, colors, typography, or borders; or when the user mentions CSS, styling, classes, Tailwind, restyle, hero section, cards, buttons, or any visual/UI changes. -- `developing-with-fortify` — Laravel Fortify headless authentication backend development. Activate when implementing authentication features including login, registration, password reset, email verification, two-factor authentication (2FA/TOTP), profile updates, headless auth, authentication scaffolding, or auth guards in Laravel applications. +- `pest-testing` — Use this skill for Pest PHP testing in Laravel projects only. Trigger whenever any test is being written, edited, fixed, or refactored — including fixing tests that broke after a code change, adding assertions, converting PHPUnit to Pest, adding datasets, and TDD workflows. Always activate when the user asks how to write something in Pest, mentions test files or directories (tests/Feature, tests/Unit, tests/Browser), or needs browser testing, smoke testing multiple pages for JS errors, or architecture tests. Covers: it()/expect() syntax, datasets, mocking, browser testing (visit/click/fill), smoke testing, arch(), Livewire component tests, RefreshDatabase, and all Pest 4 features. Do not use for factories, seeders, migrations, controllers, models, or non-test PHP code. +- `inertia-react-development` — Develops Inertia.js v2 React client-side applications. Activates when creating React pages, forms, or navigation; using , , useForm, or router; working with deferred props, prefetching, or polling; or when user mentions React with Inertia, React pages, React forms, or React navigation. +- `tailwindcss-development` — Always invoke when the user's message includes 'tailwind' in any form. Also invoke for: building responsive grid layouts (multi-column card grids, product grids), flex/grid page structures (dashboards with sidebars, fixed topbars, mobile-toggle navs), styling UI components (cards, tables, navbars, pricing sections, forms, inputs, badges), adding dark mode variants, fixing spacing or typography, and Tailwind v3/v4 work. The core use case: writing or fixing Tailwind utility classes in HTML templates (Blade, JSX, Vue). Skip for backend PHP logic, database queries, API routes, JavaScript with no HTML/CSS component, CSS file audits, build tool configuration, and vanilla CSS. +- `fortify-development` — Laravel Fortify headless authentication backend development. Activate when implementing authentication features including login, registration, password reset, email verification, two-factor authentication (2FA/TOTP), profile updates, headless auth, authentication scaffolding, or auth guards in Laravel applications. ## Conventions @@ -171,19 +173,23 @@ This project has domain-specific skills available. You MUST activate the relevan - Laravel Boost is an MCP server that comes with powerful tools designed specifically for this application. Use them. -## Artisan +## Artisan Commands -- Use the `list-artisan-commands` tool when you need to call an Artisan command to double-check the available parameters. +- Run Artisan commands directly via the command line (e.g., `php artisan route:list`, `php artisan tinker --execute "..."`). +- Use `php artisan list` to discover available commands and `php artisan [command] --help` to check parameters. ## URLs - Whenever you share a project URL with the user, you should use the `get-absolute-url` tool to ensure you're using the correct scheme, domain/IP, and port. -## Tinker / Debugging +## Debugging -- You should use the `tinker` tool when you need to execute PHP to debug code or query Eloquent models directly. - Use the `database-query` tool when you only need to read from the database. - Use the `database-schema` tool to inspect table structure before writing migrations or models. +- To execute PHP code for debugging, run `php artisan tinker --execute "your code here"` directly. +- To read configuration values, read the config files directly or run `php artisan config:show [key]`. +- To inspect routes, run `php artisan route:list` directly. +- To check environment variables, read the `.env` file directly. ## Reading Browser Logs With the `browser-logs` Tool @@ -249,7 +255,7 @@ protected function isAccessible(User $user, ?string $path = null): bool - 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. -=== inertia-laravel/v2 rules === +=== inertia-laravel/core rules === # Inertia @@ -261,14 +267,14 @@ protected function isAccessible(User $user, ?string $path = null): bool # Inertia v2 - Use all Inertia features from v1 and v2. Check the documentation before making changes to ensure the correct approach. -- New features: deferred props, infinite scrolling (merging props + `WhenVisible`), lazy loading on scroll, polling, prefetching. +- New features: deferred props, infinite scroll, merging props, polling, prefetching, once props, flash data. - When using deferred props, add an empty state with a pulsing or animated skeleton. === 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 the `list-artisan-commands` tool. +- 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. @@ -282,7 +288,7 @@ protected function isAccessible(User $user, ?string $path = null): bool ### Model Creation -- When creating new models, create useful factories and seeders for them too. Ask the user if they need any other things, using `list-artisan-commands` to check the available options to `php artisan make:model`. +- 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 @@ -319,39 +325,6 @@ protected function isAccessible(User $user, ?string $path = null): bool - 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`. -=== laravel/v12 rules === - -# Laravel 12 - -- CRITICAL: ALWAYS use `search-docs` tool for version-specific Laravel documentation and updated code examples. -- Since Laravel 11, Laravel has a new streamlined file structure which this project uses. - -## Laravel 12 Structure - -- In Laravel 12, middleware are no longer registered in `app/Http/Kernel.php`. -- Middleware are configured declaratively in `bootstrap/app.php` using `Application::configure()->withMiddleware()`. -- `bootstrap/app.php` is the file to register middleware, exceptions, and routing files. -- `bootstrap/providers.php` contains application specific service providers. -- The `app\Console\Kernel.php` file no longer exists; use `bootstrap/app.php` or `routes/console.php` for console configuration. -- Console commands in `app/Console/Commands/` are automatically available and do not require manual registration. - -## Database - -- When modifying a column, the migration must include all of the attributes that were previously defined on the column. Otherwise, they will be dropped and lost. -- Laravel 12 allows limiting eagerly loaded records natively, without external packages: `$query->latest()->limit(10);`. - -### Models - -- Casts can and likely should be set in a `casts()` method on a model rather than the `$casts` property. Follow existing conventions from other models. - -=== pennant/core rules === - -# Laravel Pennant - -- This application uses Laravel Pennant for feature flag management, providing a flexible system for controlling feature availability across different organizations and user types. -- IMPORTANT: Always use `search-docs` tool for version-specific Pennant documentation and updated code examples. -- IMPORTANT: Activate `pennant-development` every time you're working with a Pennant or feature-flag-related task. - === wayfinder/core rules === # Laravel Wayfinder @@ -378,8 +351,6 @@ Wayfinder generates TypeScript functions for Laravel routes. Import from `@/acti - This project uses Pest for testing. Create tests: `php artisan make:test --pest {name}`. - Run tests: `php artisan test --compact` or filter: `php artisan test --compact --filter=testName`. - Do NOT delete tests without approval. -- CRITICAL: ALWAYS use `search-docs` tool for version-specific Pest documentation and updated code examples. -- IMPORTANT: Activate `pest-testing` every time you're working with a Pest or testing-related task. === inertia-react/core rules === @@ -387,14 +358,6 @@ Wayfinder generates TypeScript functions for Laravel routes. Import from `@/acti - IMPORTANT: Activate `inertia-react-development` when working with Inertia React client-side patterns. -=== tailwindcss/core rules === - -# Tailwind CSS - -- Always use existing Tailwind conventions; check project patterns before adding new ones. -- IMPORTANT: Always use `search-docs` tool for version-specific Tailwind CSS documentation and updated code examples. Never rely on training data. -- IMPORTANT: Activate `tailwindcss-development` every time you're working with a Tailwind CSS or styling-related task. - === laravel/fortify rules === # Laravel Fortify diff --git a/boost.json b/boost.json index 4090c274..21faa8ab 100644 --- a/boost.json +++ b/boost.json @@ -13,11 +13,12 @@ ], "sail": false, "skills": [ + "cashier-stripe-development", "pennant-development", "wayfinder-development", "pest-testing", "inertia-react-development", "tailwindcss-development", - "developing-with-fortify" + "fortify-development" ] }