Cursor plugin for Laravel 13.x on PHP 8.3+. Pinned to laravel/framework ^13.0. Teaches the Laravel 11 skeleton (bootstrap/app.php, bootstrap/providers.php, deleted Kernel.php files), the L12 + L13 deltas (Carbon 3 mandatory, casts() method, #[Scope] attribute, Cache::flexible(), automatic eager loading, UUIDv7, image:allow_svg, FrankenPHP-default Octane), and the modern frontend stack (Livewire 4 single-file components without Volt, Inertia 3.1 deferred props, Pest 4 browser testing). Catches 37 LLM regressions with BAD / CORRECT PHP pairs.
LLMs trained on Laravel 9 / 10 (and tutorials older than the L11 skeleton flatten) emit code that does not match a fresh L13 install. They write:
app/Http/Kernel.phpwith$middleware/$middlewareGroupsarrays (deleted in L11)app/Console/Kernel.phpwithschedule()(deleted in L11)app/Providers/RouteServiceProvider.php(deleted in L11)app/Repositories/PostRepository.phpwrapping Eloquent for no benefit (Otwell, Freek, Spatie all reject this)protected $casts = [...]property instead of thecasts()method (the documented shape since L11)scopeActive($query)magic method instead of the#[Scope]attribute (the documented shape since L12)protected $guarded = []open mass-assignment combined with$request->all()wire:model.defer/wire:model.lazy(Livewire 2 syntax; renamed in v3, default-deferred in v4)class FooTest extends TestCasein a Pest 4 projectlaravel new myapp --jet/--breeze(flags removed in L12)Carbon 2method calls (Carbon 3 mandatory in L12+)'image'validation rule expecting SVG (silently rejected since L12; opt in viaimage:allow_svg)Cache::remember()on hot reads whereCache::flexible()(stale-while-revalidate) is the right answer- Manual
with()chains everywhere whenModel::automaticallyEagerLoadRelationships()(L12.8+) would cover them octane:install --server=swoolewhen FrankenPHP is the recommended runtime since 2025laravel/mixin new projects (Vite has been the default since 9.19)- Volt installation for fresh L13 + Livewire 4 (not required; SFC is built into v4)
- Reverb / Pulse described as "beta" (both GA, both ship in starter kits)
- Inertia v1 patterns with no
Inertia::defer, no<WhenVisible>, noprefetch, no polling
A short Laravel rule already lives on cursor.directory. It is shallow and has three structural problems this plugin fixes:
-
It pins nothing. No PHP version, no Laravel major. Anything from L7 to L12 is "valid" per its rules. This plugin pins
laravel/framework ^13.0onphp ^8.3explicitly. -
It actively endorses the Repository pattern verbatim (it includes the recommendation as a positive item). Taylor Otwell on the Maintainable podcast (HN: https://news.ycombinator.com/item?id=45025685), Freek Van der Herten (https://freek.dev/2422-you-might-not-need-a-repository-in-laravel-3-alternatives), and Spatie's house style (https://github.com/spatie/boost-spatie-guidelines) all reject Repository-on-top-of-Eloquent as the most common Laravel over-abstraction. This plugin's
laravel-anti-patternsrule entry #4 cites all three by name with URLs and offers Action classes as the idiomatic alternative. -
It ships flat
.cursorrulestext without globs, fixtures, skills, or an agent. This plugin ships:- 10 MDC rules with proper
globsso the Repository check fires onapp/Models/**, the Livewire check onresources/views/livewire/**, etc. - 37 documented anti-patterns with BAD / CORRECT PHP pairs (the existing rule has zero)
- 5 skills:
/laravel-new-action,/laravel-new-form-request,/laravel-migrate-v10-to-v13,/laravel-validate,/laravel-n+1-audit - 1 reviewer agent with severity grouping (CRITICAL / ERROR / WARN / NIT)
- 2 fixture projects:
correct-sample(gold-standard L13 + PHP 8.3 shape) andanti-pattern-sample(10+ tracked violations, pinned to L10 on purpose)
- 10 MDC rules with proper
Copy the rules, skills, and agent into your project's Cursor configuration. Back up your existing files first; cp -r will overwrite same-named rules.
git clone https://github.com/RoninForge/roninforge-laravel.git
# Use -n to avoid clobbering an existing customised rule of the same name.
cp -rn roninforge-laravel/rules/* your-project/.cursor/rules/
cp -rn roninforge-laravel/skills/* your-project/.cursor/skills/
cp -rn roninforge-laravel/agents/* your-project/.cursor/agents/Or vendor the whole repo as a git submodule under your-project/.cursor/plugins/. Refer to the Cursor plugin docs for the current global-install path on your Cursor version.
| Rule | Scope (globs) | What it does |
|---|---|---|
laravel-anti-patterns |
**/*.php |
37 LLM regressions with BAD / CORRECT PHP pairs. Each entry annotates which Laravel version dropped or renamed the BAD form |
laravel-core |
composer.json,bootstrap/app.php,bootstrap/providers.php,config/**/*.php |
L11 skeleton: bootstrap/app.php, bootstrap/providers.php, what is and is not in app/, removed config files, install:* commands |
laravel-eloquent |
app/Models/**/*.php,database/migrations/**/*.php |
NO Repository, casts() method, #[Scope] attribute, strict mode, automatic eager loading, UUIDv7, Carbon 3 immutability, explicit $fillable |
laravel-controllers-and-actions |
app/Http/Controllers/**/*.php,app/Http/Requests/**/*.php,app/Actions/**/*.php |
Single-action __invoke controllers, Action classes, FormRequest over inline validate, route model binding, JsonResource for APIs |
laravel-livewire-volt |
app/Livewire/**/*.php,resources/views/livewire/**/*.blade.php,resources/views/components/**/*.blade.php |
Livewire 4 single-file components (no Volt required), default-deferred wire:model, .live opt-in, lifecycle hooks, computed properties, Flux UI |
laravel-inertia |
resources/js/Pages/**/*.{tsx,jsx,vue},resources/js/Components/**/*.{tsx,jsx,vue},app/Http/Controllers/**/*.php |
Inertia 3.1 Inertia::defer, <Deferred> / <WhenVisible>, link prefetch, usePoll, useForm with processing state |
laravel-queues-and-jobs |
app/Jobs/**/*.php,app/Mail/**/*.php,app/Notifications/**/*.php,routes/console.php |
ShouldBeUnique vs WithoutOverlapping inside batches, Bus::batch / Bus::chain, Horizon, encrypted payloads, Reverb GA |
laravel-testing |
tests/**/*.php,phpunit.xml,Pest.php,pest.config.* |
Pest 4 it() / test() / expect(), browser testing replaces Dusk for most cases, fake() patterns, factories, arch tests |
laravel-security |
app/Http/**/*.php,routes/**/*.php,app/Models/**/*.php |
Explicit $fillable, FormRequest validation, validateSignatures(except: [...]) for UTM safelist, image:allow_svg opt-in, Sanctum, throttle on login |
laravel-performance |
app/**/*.php,routes/**/*.php,config/cache.php,config/octane.php |
Cache::flexible() SWR, Octane on FrankenPHP, automatic eager loading, strict mode in dev, Vite, Pulse, response cache headers, EXPLAIN |
| Skill | Command | What it does |
|---|---|---|
| New Action | /laravel-new-action |
Scaffold an Action class under app/Actions/<Verb><Noun>.php. final readonly, single __invoke, DI constructor, optional DB::transaction wrapper, optional event dispatch. Refuses Repository |
| New FormRequest | /laravel-new-form-request |
Scaffold a FormRequest with rules() (array form), authorize() (Policy-backed), messages(), prepareForValidation(). Refuses inline $request->validate() for non-trivial inputs |
| Migrate v10 -> v13 | /laravel-migrate-v10-to-v13 |
Stage-by-stage migration: composer bumps, skeleton flatten (Kernel.php deletes), Eloquent shape updates (casts(), #[Scope]), Carbon 2 -> 3, Mix -> Vite, PHPUnit -> Pest 4, frontend stack |
| Validate | /laravel-validate |
Run Pint + Larastan + Pest in parallel and grep-audit for tracked anti-patterns: Kernel.php presence, Repository classes, $casts array, scopeXxx(), $guarded = [], wire:model.defer, Mix, Carbon 2, missing strict mode |
| N+1 Audit | /laravel-n+1-audit |
Enable strict mode, surface offenders via Pulse / Telescope / Debugbar, fix with with() / withCount() / automatic eager loading, confirm with EXPLAIN, add the matching index |
| Agent | What it does |
|---|---|
laravel-reviewer |
Reviews Laravel 11/12/13 + PHP 8.3 code by severity. CRITICAL: Repository pattern, $request->all(), $guarded = [], ShouldBeUnique inside batches, regenerated Kernel.php files, PHP 8.1/8.2 pin on L13, webhook handler reading parsed JSON before HMAC. ERROR: $casts property, scopeXxx(), wire:model.defer, PHPUnit in Pest project, Carbon 2 calls, image rule expecting SVG, Mix, Filament 3 form shape. WARN: missing validateSignatures, Cache::remember where flexible fits, Octane defaulting Swoole, missing strict mode, removed config files regenerated. NIT: missing Pulse, missing $hidden, missing Carbon immutable default |
tests/fixtures/correct-sample/ is a slim Laravel 13 + PHP 8.3 project demonstrating the gold-standard skeleton: bootstrap/app.php (no Kernel.php files), bootstrap/providers.php (no five-default-providers register), explicit $fillable, casts() method, #[Scope] attribute, single-action invokable controllers, FormRequest-driven validation, Action class instead of Repository, strict mode in dev, automatic eager loading, UUIDv7 primary keys, Carbon immutability, throttled login, signed-URL UTM safelist, Pest 4 feature tests using it() / test() / expect().
tests/fixtures/anti-pattern-sample/ is the inverse. Every file violates a numbered anti-pattern. composer.json pins laravel/framework ^10.0 on purpose so the v10 skeleton (Kernel.php, RouteServiceProvider) is technically valid for that version. Tracked violations: #1 (Http/Kernel.php), #2 (Console/Kernel.php), #3 (RouteServiceProvider), #4 (Repository pattern), #5 ($casts property), #6 (scopeXxx), #9 ($request->all()), #10 (inline validate), #12 (PHPUnit class in Pest project), #20 ($guarded = []), #36 (UUIDv4 PK), plus a #24-variant composer pin combination. The fixture's README maps every violation to file:line.
Rules target laravel/framework ^13.0 on php ^8.3. Most patterns work back to L11 with the deltas called out inline (Removed in L11, Renamed in L12, Added in L12.8). Where the rule cites a version (Cache::flexible 11.23, automaticallyEagerLoadRelationships 12.8, casts() method 11.0, #[Scope] attribute 12.0, Reverb GA 1.0, Carbon 3 mandatory 12.0), verify against the changelog for the version you have installed before adopting.
MIT - see LICENSE