nwrman / laravel-toolkit
Reusable developer infrastructure for Laravel: preflight CI gates, test reporting with retry, deploy notifications, and project scaffolding.
Requires
- php: ^8.4
- illuminate/console: ^12.0 || ^13.0
- illuminate/contracts: ^12.0 || ^13.0
- illuminate/http: ^12.0 || ^13.0
- illuminate/process: ^12.0 || ^13.0
- illuminate/support: ^12.0 || ^13.0
Requires (Dev)
- orchestra/testbench: ^10.0 || ^11.0
- pestphp/pest: ^3.0 || ^4.0 || ^5.0
README
Reusable developer infrastructure for Laravel: parallel CI gates, test reporting with smart retry, deploy notifications, and project scaffolding.
Requirements
- PHP 8.5+
- Laravel 13+
Installation
composer require nwrman/laravel-toolkit
The package auto-discovers via Laravel's package discovery. Run the interactive installer to scaffold your project:
php artisan toolkit:install
The installer walks you through:
- Publishing the config file (
config/toolkit.php) - Merging recommended composer scripts into your
composer.json - Publishing AI skills & guidelines (
.ai/) - Publishing a GitHub Actions CI workflow (
.github/workflows/tests.yml) - Publishing static analysis configs (
pint.json,phpstan.neon) - Publishing deployment scripts (
scripts/)
Each step is optional and skips files that already exist.
Commands
| Command | Description |
|---|---|
toolkit:preflight |
Run all CI quality gates in parallel |
toolkit:report |
Run test suites and generate failure reports |
toolkit:retry |
Re-run only previously failed tests |
toolkit:deploy-notify |
Send deploy notifications via Telegram |
toolkit:install |
Interactive project setup wizard |
toolkit:preflight
Runs all configured quality gates in parallel with a live spinner UI, summary table, and macOS desktop notifications.
php artisan toolkit:preflight
Running 4 gates in parallel...
✓ Pest Coverage (18.4s)
✓ Frontend Coverage (8.1s)
✓ Lint (6.7s)
✓ Types (9.3s)
+--------------------+--------+-------+
| Gate | Status | Time |
+--------------------+--------+-------+
| Pest Coverage | ✓ pass | 18.4s |
| Frontend Coverage | ✓ pass | 8.1s |
| Lint | ✓ pass | 6.7s |
| Types | ✓ pass | 9.3s |
+--------------------+--------+-------+
Wall clock: 18.4s (saved 24.1s vs sequential)
✓ All gates passed!
Options:
| Option | Description |
|---|---|
--gate=<name> |
Run specific gate(s). Repeatable: --gate=lint --gate=types |
--retry |
Re-run only the gates that failed in the previous run |
--force-build |
Force a frontend rebuild before running gates |
--no-notify |
Suppress macOS desktop notification |
Retry workflow: When gates fail, the command saves state to a JSON file. On the next run with --retry, only the failed gates are re-executed:
# Initial run — lint and types fail php artisan toolkit:preflight # Fix the issues, then retry only the failed gates php artisan toolkit:preflight --retry
Agent detection: When running inside a CI agent (Claude Code, Cursor, OpenCode, etc.), the command automatically suppresses desktop notifications and uses a plain-text output format instead of ANSI spinners. Detection is powered by shipfastlabs/agent-detector.
Production safety: PreflightCommand uses Laravel's Prohibitable trait and is automatically prohibited in production environments by the service provider.
toolkit:report
Runs test suites (Pest + Vitest), parses JUnit XML results, and generates a Markdown failure report with individual test details and quick re-run commands.
php artisan toolkit:report
Options:
| Option | Description |
|---|---|
--suite=<list> |
Comma-separated suites to run: unit,feature,browser,frontend |
--force-build |
Force a frontend rebuild before browser tests |
--no-notify |
Suppress macOS desktop notification |
When run interactively without --suite, it prompts you to select which suites to run using Laravel Prompts.
On failure, two files are generated:
- Markdown report (
storage/logs/test-failures.md) — Human-readable with test names, file locations, error messages, and quick re-run commands - JSON state (
storage/logs/test-failures.json) — Machine-readable, used bytoolkit:retry
On success, both files are cleaned up automatically.
toolkit:retry
Re-runs only the tests that failed in the last toolkit:report run. Reads the JSON state file to determine which backend tests to filter and whether to re-run the frontend suite.
php artisan toolkit:retry
On success, the report and state files are cleaned up. On continued failure, they're preserved so you can iterate.
Typical workflow:
# Run all tests with reporting composer test:report # Fix failures, then re-run only what failed composer test:retry # Iterate until clean, then run full preflight composer preflight
toolkit:deploy-notify
Sends deployment status notifications to a Telegram chat via the Bot API.
php artisan toolkit:deploy-notify started
php artisan toolkit:deploy-notify succeeded
php artisan toolkit:deploy-notify failed --reason="Migration failed" --stage=build
Arguments & options:
| Argument/Option | Description |
|---|---|
status |
Required. One of: started, succeeded, failed |
--stage=<stage> |
build or deploy (default: deploy) |
--reason=<text> |
Failure reason (only used when status is failed) |
Required config in config/services.php:
'telegram' => [ 'bot_token' => env('TELEGRAM_BOT_TOKEN'), 'chat_id' => env('TELEGRAM_CHAT_ID'), 'thread_id' => env('TELEGRAM_THREAD_ID'), // optional, for topic-based groups ],
If credentials are missing, the command exits gracefully with a warning instead of failing.
toolkit:install
Interactive setup wizard that publishes stubs and merges composer scripts. Each step asks for confirmation and respects --force to overwrite existing files.
php artisan toolkit:install
php artisan toolkit:install --force # Overwrite existing files
Configuration
Publish the config file:
php artisan vendor:publish --tag=toolkit-config
Smart Merge
You only need to override what you want to change. The service provider deep-merges your published config with the package defaults using array_replace_recursive. This means you can override a single gate's command without redeclaring all gates:
// config/toolkit.php — only override what you need return [ 'gates' => [ 'lint' => [ 'command' => 'vendor/bin/pint --test', ], ], ];
All other gates (pest-coverage, frontend-coverage, types) keep their default configuration.
Removing a Gate
Set a gate to null to remove it entirely:
return [ 'gates' => [ 'frontend-coverage' => null, // removed — won't run in preflight ], ];
Config Reference
gates
Each gate runs a shell command as part of preflight. Gates run in parallel.
| Key | Type | Description |
|---|---|---|
label |
string |
Display name shown in the summary table |
command |
string |
Shell command to execute |
env |
array |
Optional environment variables (e.g., ['XDEBUG_MODE' => 'coverage']) |
requires_build |
bool |
If true, ensures frontend is built before running this gate |
Default gates:
| Gate | Label | Default Command |
|---|---|---|
pest-coverage |
Pest Coverage | vendor/bin/pest --type-coverage --min=100 && vendor/bin/pest --parallel --coverage --exactly=100.0 |
frontend-coverage |
Frontend Coverage | bun run test:coverage |
lint |
Lint | vendor/bin/pint --parallel --test && vendor/bin/rector --dry-run && bun run test:lint |
types |
Types | vendor/bin/phpstan && bun run test:types |
notifications
'notifications' => [ 'desktop' => [ 'enabled' => true, // Set to false to disable macOS notifications globally ], ],
Desktop notifications use terminal-notifier when available, falling back to afplay for sound-only alerts.
build
Controls the frontend build check that runs before gates marked with requires_build.
'build' => [ 'command' => 'vp build', // Build command 'timeout' => 120, // Timeout in seconds 'stamp_file' => 'public/build/.buildstamp', // Timestamp file for staleness check 'manifest_file' => 'public/build/manifest.json', // Vite manifest 'watch_paths' => 'resources/ vite.config.* tsconfig.* tailwind.config.* postcss.config.* package.json', ],
The build check uses find ... -newer <stamp_file> to detect source changes. If no changes are detected, the build is skipped.
paths
File paths for state and reports, relative to the project root.
'paths' => [ 'preflight_state' => 'storage/logs/preflight-result.json', 'report_dir' => 'storage/logs', 'xml_file' => 'storage/logs/test-results.xml', 'report_file' => 'storage/logs/test-failures.md', 'json_file' => 'storage/logs/test-failures.json', ],
suites
Named test suites for toolkit:report. Keys are identifiers; values are display labels.
'suites' => [ 'unit' => 'Unit', 'feature' => 'Feature', 'browser' => 'Browser', 'frontend' => 'Frontend (Vitest)', ],
frontend_test_command
Command used to run frontend tests in toolkit:report and toolkit:retry.
'frontend_test_command' => 'bun run test:ui',
Publishable Stubs
| Tag | Contents | Destination |
|---|---|---|
toolkit-config |
Configuration file | config/toolkit.php |
toolkit-static-analysis |
Pint + PHPStan configs | pint.json, phpstan.neon |
toolkit-ai |
AI coding skills & guidelines | .ai/skills/, .ai/guidelines/ |
toolkit-github |
GitHub Actions CI workflow | .github/workflows/tests.yml |
toolkit-scripts |
Deployment & lint scripts | scripts/cloud-build.sh, scripts/cloud-deploy.sh, resources/js/scripts/lint-dirty.ts |
Publish individual tags:
php artisan vendor:publish --tag=toolkit-ai php artisan vendor:publish --tag=toolkit-github php artisan vendor:publish --tag=toolkit-static-analysis php artisan vendor:publish --tag=toolkit-scripts
AI Skills
The toolkit-ai tag publishes AI coding assistant skills and guidelines to .ai/:
| Skill | Description |
|---|---|
run-preflight |
Run preflight before pushing code |
create-feature-branch |
Start a new feature branch |
finish-feature-branch |
Create a PR when implementation is complete |
land-feature-branch |
Merge an approved PR into main |
Guidelines cover: action pattern, domain folders, test enforcement, general conventions, Pest testing, React best practices, and Vitest.
Recommended Composer Scripts
The toolkit:install command can merge these scripts into your composer.json:
| Script | Command |
|---|---|
composer dev |
Concurrent queue worker + Pail + Vite dev server |
composer lint |
Rector + Pint + frontend linting |
composer lint:dirty |
Lint only files changed since last commit |
composer test |
Run all test suites (unit, feature, browser, frontend) |
composer test:unit |
Pest unit tests (parallel) |
composer test:feature |
Pest feature tests (parallel) |
composer test:browser |
Pest browser tests (parallel) |
composer test:lint |
Pint + Rector + frontend lint (verification mode) |
composer test:types |
PHPStan + frontend type checking |
composer test:ci |
Full CI pipeline (coverage + lint + types) |
composer test:report |
toolkit:report with timeout disabled |
composer test:retry |
toolkit:retry |
composer preflight |
toolkit:preflight with timeout disabled |
composer optimize |
Cache config, events, routes, and views |
composer cloud:build |
Run cloud build script |
composer cloud:deploy |
Run cloud deploy script |
Scripts are added only if the key doesn't already exist in your composer.json.
Testing
composer test
License
The MIT License (MIT). Please see License File for more information.