pelican-panel-plugins
Skill2 starsChanged 7 months agoMITView on GitHub
Write, scaffold, explain, and debug plugins for the Pelican gaming panel. Use this skill whenever the user mentions Pelican plugins, extending Pelican, FilamentPHP resources or pages for Pelican, plugin service providers, custom permissions, plugin settings, routes, models, widgets, or asks how to add new functionality to the Pelican panel.
- Reads credentials
In this repository26 other files it gives its agents.
- other/AGENTS.mdAGENTS.md
- beamng-modsSkill
- bootstrap-vueSkill
- charmbraceletSkill
- depsdotdevSkill
- find-domainSkill
- github-cliSkill
- go-cobraSkill
- goreleaserSkill
- go-viperSkill
- kasettoSkill
- laravel-actionsSkill
- lefthookSkill
- miseSkill
- php-monorepo-builderSkill
- php-saloonSkill
- prd-todo-slicerSkill
- prd-to-kandev-tasksSkill
- qodo-mergeSkill
- release-pleaseSkill
- rewrite-in-goSkill
- shieldsioSkill
- skill-researcherSkill
- skill-scoutSkill
- taskfileSkill
- website-visual-testingSkill
The file
We split this skill into the rules it gives an agent
10 rules. About 0 are common, 0 are distinctive, and 10 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈0
Distinctive · estimate
This author's own choices. Worth a look.
10
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 24
Run inside the panel directory (/var/www/pelican by default):
- Very specific: line 171
Call static methods on core classes inside a service provider's register():
- Very specific: line 266
Create a RouteServiceProvider in src/Providers/:
- Very specific: line 332
Place in lang/{locale}/ (e.g., lang/en/strings.php):
- Very specific: line 397
Namespace in plugin.json: Use \\ (double backslash) for namespace separators
- Very specific: line 398
Migration naming: Use numeric prefixes (001_, 002_) to control execution order
- Very specific: line 399
Environment variables: Always prefix with your plugin ID (e.g., MY_PLUGIN_*)
- Very specific: line 400
Panel context: Use Filament::getTenant() to get current server in server panel
- Very specific: line 402
Relation managers: Must be registered on core resources via registerCustomRelations() in service provider's register() method
- Very specific: line 403
Testing: Use php artisan migrate:fresh --seed to reset and test migrations
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Pelican Panel Plugins
Pelican is an open-source game server management panel built on Laravel + FilamentPHP. Plugins let you add functionality without touching core files.
⚠️ The plugin system is still in active development — some features may change.
Quick Reference
📖 Detailed Guides:
- FilamentPHP Patterns - Resources, pages, widgets, actions, relation managers
- Advanced Patterns - Models, services, routes, commands, events
- Complete Plugin Walkthrough - Step-by-step example
Scaffolding a New Plugin
Run inside the panel directory (/var/www/pelican by default):
php artisan p:plugin:makeThis creates the basic structure with plugin.json, main plugin class, service provider, and config file.
Critical: The plugin folder name must exactly match the
idfield inplugin.json.
Plugin Structure
plugins/my-plugin/
├── plugin.json # Metadata and configuration
├── config/
│ └── my-plugin.php # Config values (use env vars)
├── database/
│ └── migrations/ # Auto-discovered migrations
├── lang/ # Translations (namespaced: my-plugin::strings.key)
├── resources/
│ └── views/ # Blade views (namespaced: my-plugin::view-name)
├── routes/ # Optional route files
└── src/ # App logic (PSR-4 autoloaded)
├── MyPlugin.php # Main plugin class
├── Filament/
│ ├── Admin/ # Admin panel components
│ │ ├── Pages/
│ │ ├── Resources/
│ │ └── Widgets/
│ ├── App/ # Server list panel
│ └── Server/ # Server management panel
├── Models/
├── Policies/ # Auto-discovered
├── Providers/ # Auto-discovered service providers
├── Console/Commands/ # Auto-discovered artisan commands
└── Http/
└── Controllers/Everything in standard Laravel locations is auto-discovered: migrations, providers, commands, policies.
plugin.json
{
"id": "my-plugin",
"name": "My Plugin",
"author": "Your Name",
"version": "1.0.0",
"description": "Short description",
"category": "plugin",
"namespace": "MyName\\MyPlugin",
"class": "MyPlugin",
"panels": ["admin", "server"],
"panel_version": "^1.2.0",
"composer_packages": {
"vendor/package": "^1.0"
}
}| Field | Required | Notes |
id | ✅ | Must match folder name |
namespace | ✅ | PHP namespace root (use \\ for backslashes) |
class | ✅ | Main class name (in src/) |
category | ✅ | plugin, theme, or language |
panels | No | Array of panel IDs or omit for all panels |
panel_version | No | Minimum panel version (e.g., ^1.2.0) |
composer_packages | No | External dependencies |
Main Plugin Class
Located in src/{ClassName}.php:
namespace MyName\MyPlugin;
use Filament\Contracts\Plugin;
use Filament\Panel;
class MyPlugin implements Plugin
{
public function getId(): string
{
return 'my-plugin';
}
public function register(Panel $panel): void
{
$id = str($panel->getId())->title(); // "Admin", "App", "Server"
// Auto-discover Filament components
$panel->discoverPages(
plugin_path($this->getId(), "src/Filament/$id/Pages"),
"MyName\\MyPlugin\\Filament\\$id\\Pages"
);
$panel->discoverResources(
plugin_path($this->getId(), "src/Filament/$id/Resources"),
"MyName\\MyPlugin\\Filament\\$id\\Resources"
);
$panel->discoverWidgets(
plugin_path($this->getId(), "src/Filament/$id/Widgets"),
"MyName\\MyPlugin\\Filament\\$id\\Widgets"
);
}
public function boot(Panel $panel): void
{
//
}
}The Three Filament Panels
| Panel ID | Area | Use Case |
admin | Admin area | Full CRUD for resources, settings, management |
app | Server list | Minimal UI (no nav by default) |
server | Server management | Tenant-scoped (current server context) |
Enabling Navigation on app Panel
use App\Filament\App\Resources\Servers\ServerResource;
use App\Enums\CustomizationKey;
public function register(Panel $panel): void
{
parent::register($panel);
if ($panel->getId() === 'app') {
ServerResource::embedServerList();
$panel->navigation(true);
$panel->topbar(function () {
$nav = user()?->getCustomization(CustomizationKey::TopNavigation);
return in_array($nav, ['topbar', 'mixed', true], true);
});
$panel->clearCachedComponents();
}
}Extending Core Resources
Call static methods on core classes inside a service provider's register():
use App\Filament\Admin\Resources\Users\UserResource;
use App\Filament\Admin\Resources\Servers\ServerResource;
use App\Models\Role;
public function register(): void
{
// Add a relation manager tab
ServerResource::registerCustomRelations(MyRelationManager::class);
// Register permissions
Role::registerCustomDefaultPermissions('myModel');
Role::registerCustomModelIcon('myModel', 'tabler-star');
}Available customization traits (check app/Traits/Filament/ for all):
CanModifyResource- Relation managers, custom actionsCanCustomizePage- Widgets, header actionsCanModifyForm/CanModifyTable- Form/table hooks
Plugin Settings
Implement HasPluginSettings on your main class:
use App\Contracts\Plugins\HasPluginSettings;
use App\Traits\EnvironmentWriterTrait;
use Filament\Forms\Components\TextInput;
use Filament\Notifications\Notification;
class MyPlugin implements Plugin, HasPluginSettings
{
use EnvironmentWriterTrait;
public function getSettingsForm(): array
{
return [
TextInput::make('api_key')
->required()
->default(fn () => config('my-plugin.api_key')),
];
}
public function saveSettings(array $data): void
{
$this->writeToEnvironment([
'MY_PLUGIN_API_KEY' => $data['api_key'],
]);
Notification::make()->title('Settings saved')->success()->send();
}
}Always prefix env vars with your plugin ID to avoid conflicts.
Permissions
Admin Role Permissions
In your service provider's register():
use App\Models\Role;
// Shorthand: registers viewList, view, create, update, delete
Role::registerCustomDefaultPermissions('myModel');
// Custom permissions
Role::registerCustomPermissions([
'myModel' => ['export', 'approve'],
'server' => ['customAction'], // extend existing model
]);
// Optional: icon for permission group
Role::registerCustomModelIcon('myModel', 'tabler-star');Subuser Permissions
use App\Models\Subuser;
// New permission group
Subuser::registerCustomPermissions('myFeature', ['read', 'write'], 'tabler-bolt', false);
// Append to existing group
Subuser::registerCustomPermissions('console', ['myCustomAction']);Routes
Create a RouteServiceProvider in src/Providers/:
use Illuminate\Foundation\Support\Providers\RouteServiceProvider;
use Illuminate\Support\Facades\Route;
class MyPluginRoutesProvider extends RouteServiceProvider
{
public function boot(): void
{
$this->routes(function () {
// Simple route
Route::get('/my-plugin/test', [TestController::class, 'index'])
->name('my-plugin.test');
// Load from file
Route::prefix('/my-plugin')
->group(plugin_path('my-plugin', 'routes/web.php'));
// Append to client API
Route::middleware(['api', 'client-api', 'throttle:api.client'])
->prefix('/api/client/servers/{server}')
->scopeBindings()
->group(plugin_path('my-plugin', 'routes/api-client.php'));
});
}
}Models & Relationships
Add Relationship to Core Models
In your service provider's boot():
use App\Models\Server;
use MyPlugin\Models\Ticket;
public function boot(): void
{
Server::resolveRelationUsing('tickets', fn (Server $server) =>
$server->hasMany(Ticket::class, 'server_id', 'id')
);
}Now $server->tickets works everywhere.
Policies
use App\Policies\DefaultAdminPolicies;
class MyModelPolicy
{
use DefaultAdminPolicies;
protected string $modelName = 'myModel';
}This automatically checks admin role permissions based on the registered model name.
Translations
Place in lang/{locale}/ (e.g., lang/en/strings.php):
return [
'welcome' => 'Welcome',
'item' => 'Item|Items', // Pluralization
];Usage:
trans('my-plugin::strings.welcome')
trans_choice('my-plugin::strings.item', 2) // "Items"Views
Place in resources/views/:
view('my-plugin::my-view')
// → plugins/my-plugin/resources/views/my-view.blade.phpCommon Patterns
FilamentPHP Components
See FilamentPHP Patterns for:
- Resources (CRUD interfaces)
- Pages (custom pages)
- Widgets (dashboard components)
- Relation Managers (manage related records)
- Custom Actions (reusable buttons)
- Form components (inputs, selects, toggles)
- Table columns and filters
Advanced Patterns
See Advanced Patterns for:
- Model events and hooks
- Enums with Filament interfaces
- Service classes
- HTTP controllers and API routes
- Artisan commands and scheduling
- HTTP macros for external APIs
- Database migrations
- Error handling
Complete Example
See Complete Plugin Walkthrough for a step-by-step guide building a "Server Notes" plugin.
Publishing a Plugin
- Open
plugin.jsonand remove themetablock (internal use only) - Zip the entire plugin folder
- Share the zip — users install via panel UI Import button or manually drop into
plugins/
Publish to the community:
- GitHub: pelican-dev/plugins
- Discord:
#pluginschannel at discord.gg/pelican-panel
Tips & Gotchas
- Namespace in plugin.json: Use
\\(double backslash) for namespace separators - Migration naming: Use numeric prefixes (
001_,002_) to control execution order - Environment variables: Always prefix with your plugin ID (e.g.,
MY_PLUGIN_*) - Panel context: Use
Filament::getTenant()to get current server in server panel - Auto-discovery: Service providers, commands, migrations, and policies are auto-discovered
- Relation managers: Must be registered on core resources via
registerCustomRelations()in service provider'sregister()method - Testing: Use
php artisan migrate:fresh --seedto reset and test migrations - Permissions: Register in service provider's
register(), notboot()
Getting Help
- Documentation: pelican.dev/docs/panel
- Discord: discord.gg/pelican-panel
- GitHub: github.com/pelican-dev/panel
- Example Plugins: github.com/pelican-dev/plugins
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

