Files
shell-leaderboard/CLAUDE.md
2026-03-24 17:01:12 +00:00

20 KiB

AI Agent Instructions for Laravel Module Development

CRITICAL: This document provides EXACT specifications for building modules in this Laravel template. Follow these instructions PRECISELY. Do NOT deviate, guess, or improvise. When in doubt, READ THE EXISTING CODE before making changes.


🚨 BEFORE YOU START - MANDATORY STEPS

1. Always Check Existing Patterns First

# List existing modules to see patterns
ls -la src/app/Modules/

# Read an existing module's structure
find src/app/Modules/[ExistingModule]/ -type f -name "*.php"

2. Always Check Logs After Errors

docker-compose exec app tail -n 100 storage/logs/laravel.log

3. Never Guess at Solutions

  • If you encounter an error, READ the full stack trace
  • Find the EXACT file and line number causing the issue
  • Understand what the code is trying to do before fixing

📁 PROJECT STRUCTURE - EXACT PATHS

Laravel-Docker-Dev-Template/
├── src/                              # ← ALL Laravel code goes here
│   ├── app/
│   │   ├── Filament/
│   │   │   ├── Pages/
│   │   │   │   └── Settings.php      # Site settings page
│   │   │   └── Resources/            # Core Filament resources
│   │   │       ├── UserResource.php
│   │   │       ├── RoleResource.php
│   │   │       └── PermissionResource.php
│   │   ├── Http/
│   │   │   ├── Controllers/
│   │   │   └── Middleware/
│   │   ├── Models/
│   │   │   ├── User.php
│   │   │   └── Setting.php
│   │   ├── Modules/                  # ← ALL modules go here
│   │   │   └── [ModuleName]/         # ← Each module is a folder
│   │   ├── Providers/
│   │   │   └── AppServiceProvider.php
│   │   └── Traits/
│   │       └── ModuleAuditable.php
│   ├── bootstrap/
│   │   └── app.php                   # Middleware registration
│   ├── config/
│   ├── database/
│   │   ├── migrations/
│   │   └── seeders/
│   │       ├── DatabaseSeeder.php
│   │       └── RolePermissionSeeder.php
│   ├── resources/
│   │   └── views/
│   │       ├── layouts/
│   │       │   ├── app.blade.php     # Authenticated layout
│   │       │   └── guest.blade.php   # Guest layout
│   │       └── components/
│   └── routes/
│       ├── web.php
│       ├── api.php
│       └── auth.php
├── docker-compose.yml
├── setup.bat                         # Windows setup
└── setup.sh                          # Linux/Mac setup

🔧 MODULE STRUCTURE - EXACT SPECIFICATION

When creating a module named [ModuleName] (e.g., StockManagement):

src/app/Modules/[ModuleName]/
├── Config/
│   └── [module_name].php             # snake_case filename
├── Database/
│   ├── Migrations/
│   │   └── YYYY_MM_DD_HHMMSS_create_[table_name]_table.php
│   └── Seeders/
│       └── [ModuleName]Seeder.php
├── Filament/
│   └── Resources/
│       ├── [ModelName]Resource.php
│       └── [ModelName]Resource/
│           └── Pages/
│               ├── List[ModelName]s.php
│               ├── Create[ModelName].php
│               └── Edit[ModelName].php
├── Http/
│   ├── Controllers/
│   │   └── [ModuleName]Controller.php
│   ├── Middleware/
│   └── Requests/
│       └── [ModelName]Request.php
├── Models/
│   └── [ModelName].php
├── Policies/
│   └── [ModelName]Policy.php
├── Services/
│   └── [ModelName]Service.php
├── Routes/
│   ├── web.php
│   └── api.php
├── Resources/
│   └── views/
│       ├── index.blade.php
│       └── layouts/
│           └── module.blade.php
├── Permissions.php
└── [ModuleName]ServiceProvider.php

📝 EXACT FILE TEMPLATES

1. Service Provider (REQUIRED)

File: src/app/Modules/[ModuleName]/[ModuleName]ServiceProvider.php

<?php

namespace App\Modules\[ModuleName];

use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Route;

class [ModuleName]ServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        $this->mergeConfigFrom(
            __DIR__ . '/Config/[module_name].php',
            '[module_name]'
        );
    }

    public function boot(): void
    {
        $this->loadMigrationsFrom(__DIR__ . '/Database/Migrations');
        $this->loadViewsFrom(__DIR__ . '/Resources/views', '[module-slug]');
        
        $this->registerRoutes();
    }

    protected function registerRoutes(): void
    {
        Route::middleware(['web', 'auth'])
            ->prefix('[module-slug]')
            ->name('[module-slug].')
            ->group(__DIR__ . '/Routes/web.php');

        if (file_exists(__DIR__ . '/Routes/api.php')) {
            Route::middleware(['api', 'auth:sanctum'])
                ->prefix('api/[module-slug]')
                ->name('api.[module-slug].')
                ->group(__DIR__ . '/Routes/api.php');
        }
    }
}

NAMING RULES:

  • [ModuleName] = PascalCase (e.g., StockManagement)
  • [module_name] = snake_case (e.g., stock_management)
  • [module-slug] = kebab-case (e.g., stock-management)

2. Config File (REQUIRED)

File: src/app/Modules/[ModuleName]/Config/[module_name].php

<?php

return [
    'name' => '[Module Display Name]',
    'slug' => '[module-slug]',
    'version' => '1.0.0',
    
    'audit' => [
        'enabled' => true,
        'strategy' => 'all',  // 'all', 'include', 'exclude', 'none'
        'include' => [],
        'exclude' => [],
    ],
];

3. Model (REQUIRED for data modules)

File: src/app/Modules/[ModuleName]/Models/[ModelName].php

<?php

namespace App\Modules\[ModuleName]\Models;

use App\Traits\ModuleAuditable;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use OwenIt\Auditing\Contracts\Auditable;

class [ModelName] extends Model implements Auditable
{
    use HasFactory, ModuleAuditable;

    protected $fillable = [
        // List ALL fields that can be mass-assigned
    ];

    protected $casts = [
        // Type casts for fields
    ];

    // Define relationships here
}

4. Migration (REQUIRED for data modules)

File: src/app/Modules/[ModuleName]/Database/Migrations/YYYY_MM_DD_HHMMSS_create_[table_name]_table.php

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    public function up(): void
    {
        Schema::create('[table_name]', function (Blueprint $table) {
            $table->id();
            // Define ALL columns here
            $table->timestamps();
        });
    }

    public function down(): void
    {
        Schema::dropIfExists('[table_name]');
    }
};

5. Permissions (REQUIRED) - AUTO-LOADED

File: src/app/Modules/[ModuleName]/Permissions.php

<?php

return [
    '[module_name].view' => 'View [Module Name]',
    '[module_name].create' => 'Create [Module Name] records',
    '[module_name].edit' => 'Edit [Module Name] records',
    '[module_name].delete' => 'Delete [Module Name] records',
];

AUTO-LOADED: RolePermissionSeeder automatically scans all app/Modules/*/Permissions.php files and registers them. Just run php artisan db:seed --class=RolePermissionSeeder.

6. Filament Resource (REQUIRED for admin)

File: src/app/Modules/[ModuleName]/Filament/Resources/[ModelName]Resource.php

<?php

namespace App\Modules\[ModuleName]\Filament\Resources;

use App\Modules\[ModuleName]\Filament\Resources\[ModelName]Resource\Pages;
use App\Modules\[ModuleName]\Models\[ModelName];
use Filament\Forms;
use Filament\Forms\Form;
use Filament\Resources\Resource;
use Filament\Tables;
use Filament\Tables\Table;

class [ModelName]Resource extends Resource
{
    protected static ?string $model = [ModelName]::class;

    protected static ?string $navigationIcon = 'heroicon-o-rectangle-stack';

    protected static ?string $navigationGroup = '[Module Display Name]';

    protected static ?int $navigationSort = 1;

    public static function canAccess(): bool
    {
        return auth()->user()?->can('[module_name].view') ?? false;
    }

    public static function form(Form $form): Form
    {
        return $form
            ->schema([
                Forms\Components\Section::make('[Model Name] Details')
                    ->schema([
                        // Add form fields here
                    ]),
            ]);
    }

    public static function table(Table $table): Table
    {
        return $table
            ->columns([
                // Add table columns here
            ])
            ->filters([
                //
            ])
            ->actions([
                Tables\Actions\EditAction::make(),
                Tables\Actions\DeleteAction::make(),
            ])
            ->bulkActions([
                Tables\Actions\BulkActionGroup::make([
                    Tables\Actions\DeleteBulkAction::make(),
                ]),
            ]);
    }

    public static function getRelations(): array
    {
        return [
            //
        ];
    }

    public static function getPages(): array
    {
        return [
            'index' => Pages\List[ModelName]s::route('/'),
            'create' => Pages\Create[ModelName]::route('/create'),
            'edit' => Pages\Edit[ModelName]::route('/{record}/edit'),
        ];
    }
}

7. Filament Resource Pages (REQUIRED)

File: src/app/Modules/[ModuleName]/Filament/Resources/[ModelName]Resource/Pages/List[ModelName]s.php

<?php

namespace App\Modules\[ModuleName]\Filament\Resources\[ModelName]Resource\Pages;

use App\Modules\[ModuleName]\Filament\Resources\[ModelName]Resource;
use Filament\Actions;
use Filament\Resources\Pages\ListRecords;

class List[ModelName]s extends ListRecords
{
    protected static string $resource = [ModelName]Resource::class;

    protected function getHeaderActions(): array
    {
        return [
            Actions\CreateAction::make(),
        ];
    }
}

File: src/app/Modules/[ModuleName]/Filament/Resources/[ModelName]Resource/Pages/Create[ModelName].php

<?php

namespace App\Modules\[ModuleName]\Filament\Resources\[ModelName]Resource\Pages;

use App\Modules\[ModuleName]\Filament\Resources\[ModelName]Resource;
use Filament\Resources\Pages\CreateRecord;

class Create[ModelName] extends CreateRecord
{
    protected static string $resource = [ModelName]Resource::class;
}

File: src/app/Modules/[ModuleName]/Filament/Resources/[ModelName]Resource/Pages/Edit[ModelName].php

<?php

namespace App\Modules\[ModuleName]\Filament\Resources\[ModelName]Resource\Pages;

use App\Modules\[ModuleName]\Filament\Resources\[ModelName]Resource;
use Filament\Actions;
use Filament\Resources\Pages\EditRecord;

class Edit[ModelName] extends EditRecord
{
    protected static string $resource = [ModelName]Resource::class;

    protected function getHeaderActions(): array
    {
        return [
            Actions\DeleteAction::make(),
        ];
    }
}

8. Routes (REQUIRED)

File: src/app/Modules/[ModuleName]/Routes/web.php

<?php

use App\Modules\[ModuleName]\Http\Controllers\[ModuleName]Controller;
use Illuminate\Support\Facades\Route;

Route::get('/', [[ModuleName]Controller::class, 'index'])->name('index');

File: src/app/Modules/[ModuleName]/Routes/api.php (if API needed)

<?php

use Illuminate\Support\Facades\Route;

Route::middleware('auth:sanctum')->group(function () {
    // API routes here
});

9. Controller (REQUIRED)

File: src/app/Modules/[ModuleName]/Http/Controllers/[ModuleName]Controller.php

<?php

namespace App\Modules\[ModuleName]\Http\Controllers;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;

class [ModuleName]Controller extends Controller
{
    public function index()
    {
        $this->authorize('[module_name].view');
        
        return view('[module-slug]::index');
    }
}

10. Index View (REQUIRED)

File: src/app/Modules/[ModuleName]/Resources/views/index.blade.php

<x-app-layout>
    <x-slot name="header">
        <h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
            {{ __('[Module Display Name]') }}
        </h2>
    </x-slot>

    <div class="py-12">
        <div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
            <div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
                <div class="p-6 text-gray-900 dark:text-gray-100">
                    {{ __('[Module Display Name] content goes here.') }}
                </div>
            </div>
        </div>
    </div>
</x-app-layout>

MODULE REGISTRATION

After creating all files, register the module in src/config/app.php providers array:

'providers' => ServiceProvider::defaultProviders()->merge([
    // ...existing providers...
    App\Modules\[ModuleName]\[ModuleName]ServiceProvider::class,
])->toArray(),

Or add to src/bootstrap/providers.php:

return [
    App\Providers\AppServiceProvider::class,
    // ...existing providers...
    App\Modules\[ModuleName]\[ModuleName]ServiceProvider::class,
];

🔄 AFTER MODULE CREATION - REQUIRED COMMANDS

# 1. Run migrations
docker-compose exec app php artisan migrate

# 2. Clear all caches
docker-compose exec app php artisan optimize:clear

# 3. Seed permissions (if using permissions)
docker-compose exec app php artisan db:seed --class=RolePermissionSeeder

# 4. Reset permission cache
docker-compose exec app php artisan permission:cache-reset

⚠️ COMMON MISTAKES TO AVOID

1. Wrong Namespace

namespace App\Modules\StockManagement\Models; namespace App\Modules\StockManagement\Models;

The namespace MUST match the folder structure EXACTLY.

2. Wrong View Namespace

return view('stockmanagement::index'); return view('StockManagement::index'); return view('stock-management::index');

View namespace is ALWAYS kebab-case.

3. Wrong Permission Names

'StockManagement.view' 'stock-management.view' 'stock_management.view'

Permissions are ALWAYS snake_case.

4. Forgetting to Register Provider

The module WILL NOT LOAD if you forget to add its ServiceProvider.

5. Wrong Import Paths

use App\Models\Product; use App\Modules\StockManagement\Models\Product;

Module models are in the MODULE namespace, not the core App namespace.

6. Missing Fillable Array

Empty $fillable array causes mass-assignment errors List ALL fields that should be mass-assignable

7. Forgetting to Run Migrations

Always run php artisan migrate after creating migrations.


🎨 FILAMENT FORM FIELD REFERENCE

// Text
Forms\Components\TextInput::make('name')
    ->required()
    ->maxLength(255);

// Email
Forms\Components\TextInput::make('email')
    ->email()
    ->required();

// Password
Forms\Components\TextInput::make('password')
    ->password()
    ->required();

// Textarea
Forms\Components\Textarea::make('description')
    ->rows(3);

// Select
Forms\Components\Select::make('status')
    ->options([
        'active' => 'Active',
        'inactive' => 'Inactive',
    ])
    ->required();

// Checkbox
Forms\Components\Toggle::make('is_active')
    ->default(true);

// Date
Forms\Components\DatePicker::make('date');

// DateTime
Forms\Components\DateTimePicker::make('published_at');

// Number
Forms\Components\TextInput::make('price')
    ->numeric()
    ->prefix('$');

// File Upload
Forms\Components\FileUpload::make('image')
    ->image()
    ->directory('uploads');

// Relationship Select
Forms\Components\Select::make('category_id')
    ->relationship('category', 'name')
    ->searchable()
    ->preload();

📊 FILAMENT TABLE COLUMN REFERENCE

// Text
Tables\Columns\TextColumn::make('name')
    ->searchable()
    ->sortable();

// Badge
Tables\Columns\TextColumn::make('status')
    ->badge()
    ->color(fn (string $state): string => match ($state) {
        'active' => 'success',
        'inactive' => 'danger',
        default => 'gray',
    });

// Boolean Icon
Tables\Columns\IconColumn::make('is_active')
    ->boolean();

// Image
Tables\Columns\ImageColumn::make('avatar')
    ->circular();

// Date
Tables\Columns\TextColumn::make('created_at')
    ->dateTime()
    ->sortable();

// Money
Tables\Columns\TextColumn::make('price')
    ->money('USD');

🔐 EXISTING CORE RESOURCES

User Management (already exists in template)

  • src/app/Filament/Resources/UserResource.php - User CRUD with role assignment
  • src/app/Filament/Resources/RoleResource.php - Role CRUD with permissions
  • src/app/Filament/Resources/PermissionResource.php - Permission CRUD

Settings (already exists)

  • src/app/Filament/Pages/Settings.php - Site settings (name, logo, colors, registration toggle)
  • src/app/Models/Setting.php - Settings model with get/set helpers

Authentication (pre-installed)

  • Laravel Breeze with Blade templates
  • Login: /login
  • Register: /register (controlled by enable_registration setting)
  • Admin: /admin

🧪 TESTING MODULES

Create tests in src/tests/Feature/Modules/[ModuleName]/:

<?php

namespace Tests\Feature\Modules\[ModuleName];

use App\Models\User;
use App\Modules\[ModuleName]\Models\[ModelName];
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class [ModelName]Test extends TestCase
{
    use RefreshDatabase;

    public function test_user_can_view_[model_name]s(): void
    {
        $user = User::factory()->create();
        $user->givePermissionTo('[module_name].view');

        $this->actingAs($user)
            ->get('/[module-slug]')
            ->assertStatus(200);
    }
}

📋 CHECKLIST FOR NEW MODULES

Before considering a module complete:

  • ServiceProvider created and registered
  • Config file created
  • Model created with fillable and casts
  • Migration created and run
  • Permissions.php created
  • Filament Resource created with all pages
  • Controller created
  • Routes (web.php and optionally api.php) created
  • Index view created
  • php artisan migrate run
  • php artisan optimize:clear run
  • php artisan db:seed --class=RolePermissionSeeder run
  • Module accessible at /[module-slug]
  • Admin panel shows module in navigation
  • CRUD operations work in admin

🆘 DEBUGGING

Module Not Loading

# Check if provider is registered
docker-compose exec app php artisan about

# Clear everything
docker-compose exec app php artisan optimize:clear

Filament Resource Not Showing

# Clear Filament cache
docker-compose exec app php artisan filament:cache-components

# Check canAccess() method returns true
# Check user has required permission

Permission Denied

# Reset permission cache
docker-compose exec app php artisan permission:cache-reset

# Verify permissions exist
docker-compose exec app php artisan tinker
>>> \Spatie\Permission\Models\Permission::pluck('name');

View Not Found

# Verify view namespace (must be kebab-case)
# Check file exists in Resources/views/

Remember: When in doubt, look at existing code in the codebase. The patterns are already established - follow them exactly.