Artisan for Laravel packages. Run every make: generator from your package directory and get files in src/ with your package's namespace — models, commands, migrations, factories, tests, and full Filament resources included.
Built on Orchestra Testbench and Canvas, the tools that already power Laravel package testing. Partisan adds the missing piece: generators that understand your package is the app.
composer require --dev packstub/partisan
vendor/bin/partisan make:model Invoice
# → src/Models/Invoice.php with `namespace Acme\Widget\Models;`Partisan reads your package's composer.json and points the whole generator suite at it:
| Command | Destination | Namespace |
|---|---|---|
make:model Invoice |
src/Models/Invoice.php |
Acme\Widget\Models |
make:command SyncInvoices |
src/Console/SyncInvoices.php |
Acme\Widget\Console |
make:migration create_invoices_table |
database/migrations/… |
— |
make:factory InvoiceFactory |
database/factories/… |
Acme\Widget\Database\Factories |
make:seeder InvoiceSeeder |
database/seeders/… |
Acme\Widget\Database\Seeders |
make:test InvoiceTest |
tests/Feature/… |
Acme\Widget\Tests\Feature |
make:filament-resource Invoice |
src/Filament/Resources/… |
Acme\Widget\Filament\Resources\… |
…and the rest of the make: family (casts, controllers, events, jobs, listeners, mail, middleware, notifications, policies, providers, requests, rules, views, components, enums, interfaces, traits, and more). Generated feature tests extend your package's own tests/TestCase.php when you have one, or Orchestra\Testbench\TestCase otherwise.
Because partisan wraps the Testbench CLI, everything Testbench can do still works — vendor/bin/partisan serve, migrate, route:list, your package's own commands, workbench:install, all of it.
Testbench boots a skeleton Laravel application around your package; Canvas routes every generator through a pluggable generator preset. Partisan registers a preset that maps the generator targets onto your package:
autoload.psr-4→ source path and root namespaceautoload-dev.psr-4→ tests path and namespacedatabase/,resources/→ database and view targetsextra.laravel.providers→ auto-registered, so your package's own artisan commands are available too
The workbench and laravel presets stay available per command (make:model Demo --preset=workbench) when you want to generate into your Workbench demo app instead.
Run vendor/bin/partisan partisan:about to see exactly how your package was mapped.
vendor/bin/partisan is a lot of typing for a tool you reach for constantly. The installer sets up either (or both) of the shortcuts below — each step is a prompt you can decline:
vendor/bin/partisan partisan:installIn scripts and CI (--no-interaction) it only does what you flag explicitly: --link, --alias.
The installer's first offer is an artisan script in your package root — a two-line stub that forwards to vendor/bin/partisan:
#!/usr/bin/env php
<?php require __DIR__.'/vendor/bin/partisan';With it, the muscle-memory commands work in your package exactly like in an app:
php artisan make:model Invoice
./artisan make:filament-resource InvoiceA plain PHP file works the same on macOS, Linux, and Windows, and survives archives and checkouts without symlink support — but if you prefer the classic look, a symlink does the job too: ln -s vendor/bin/partisan artisan. Commit the script so every contributor gets it, or let the installer add it to .gitignore if you'd rather keep it personal.
The second offer is a pa shortcut appended to your shell profile (zsh, bash, and fish are detected). To set it up by hand instead, add an alias:
# ~/.zshrc or ~/.bashrc
alias pa="vendor/bin/partisan"Then, from your package root:
pa make:model InvoiceIf you often work from subdirectories, use a function that finds the nearest vendor/bin/partisan upward instead (this is the variant the installer offers first):
pa() {
local dir=$PWD
while [ "$dir" != "/" ]; do
[ -x "$dir/vendor/bin/partisan" ] && { "$dir/vendor/bin/partisan" "$@"; return; }
dir=$(dirname "$dir")
done
echo "no vendor/bin/partisan here — composer require --dev packstub/partisan" >&2
return 1
}If your package targets a Filament panel, declare a panel provider (in extra.laravel.providers) whose discovery paths use app_path() — partisan remaps app_path() to src/, so Filament's generators follow along:
$panel->discoverResources(in: app_path('Filament/Resources'), for: 'Acme\\Widget\\Filament\\Resources')Then make:filament-resource Invoice produces the resource, pages, schema, and table classes inside src/Filament/Resources under your namespace.
Partisan needs none for the common case. It honors your testbench.yaml (providers, custom skeleton, migrations, seeders) exactly like the Testbench CLI. Set PARTISAN_DISCOVER=0 to skip auto-registering the providers from extra.laravel.providers.
make:command SyncInvoicesoffers to register the new command in your package's service provider (automatic under--no-interaction, opt out with--no-register). It inserts into an existing$this->commands([...])or spatie-style->hasCommands([...])block — adding the import, never duplicating an entry — and appends a fresh$this->commands([...])block toboot()when the provider has neither.make:model Invoice --factorywires the model to its package factory automatically: theHasFactorydocblock points at yourDatabase\Factoriesnamespace and a#[UseFactory(InvoiceFactory::class)]attribute is added, soInvoice::factory()resolves at runtime with no manualnewFactory(). If a future Laravel stub ships its own factory wiring, partisan detects it and only corrects the namespace.make:providerwrites the class intosrc/Providers; registering it in your package's service provider chain is up to you.
composer testThe suite generates every supported file type into a disposable fixture package and asserts the paths and namespaces, for both Laravel and Filament generators.
MIT