playerarm123 / laravel-workflow-kit
The rules, generators and checks that keep every Laravel project built the same way.
Package info
github.com/playerarm123/laravel-workflow-kit
pkg:composer/playerarm123/laravel-workflow-kit
Requires
- php: ^8.4
- laravel/framework: ^13.0
- spatie/laravel-data: ^4.23
Requires (Dev)
- inertiajs/inertia-laravel: ^3.0
- larastan/larastan: ^3.9
- laravel/pint: ^1.27
- orchestra/testbench: ^11.3
- pestphp/pest: ^5.1
- pestphp/pest-plugin-laravel: ^5.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
- dev-main
- v0.2.16
- v0.2.15
- v0.2.14
- v0.2.13
- v0.2.12
- v0.2.11
- v0.2.10
- v0.2.9
- v0.2.8
- v0.2.7
- v0.2.6
- v0.2.5
- v0.2.4
- v0.2.3
- v0.2.2
- v0.2.0
- v0.1.1
- v0.1.0
- dev-claude/structure-canvas
- dev-claude/funny-carson-9xiyql
- dev-feat/structure-value-object-methods
- dev-feat/shared-time-values
- dev-feat/structure-screen-ui
- dev-fix/merchant-slice-bugs
- dev-feat/structure-sync
- dev-feat/structure-exceptions
This package is auto-updated.
Last update: 2026-10-26 15:19:36 UTC
README
The rules, generators and checks that keep every Laravel project built the same way. One dev dependency ships:
- Guidelines and skills for AI agents and people, written into
CLAUDE.md/AGENTS.mdby Laravel Boost. - Generators (
make:*) that write each piece the way the guidelines want it. - The structure manifest (
kit:import,kit:plan,kit:apply,kit:retire) and a local screen to design with it. - Checks that hold the code to the rules: the
Architecturetest suite, the ESLint rules and a PHPStan rule. - The kit's project files (base classes, list and form hooks, the audit log page, …), written by
kit:installand kept identical to the kit's copy.
The rules themselves live in resources/boost/guidelines. This file only says how to install the kit and how to use it day to day.
Requirements
- PHP 8.4, Composer, Node 22.18+ or 24.11+ (what the starter kit's
vite-plusneeds; npm warnsEBADENGINEon an older 22). - A PostgreSQL, MySQL or MariaDB server. The kit never runs on sqlite: tests run on the engine production runs.
- A project made from Laravel's React starter kit (Laravel 13, Inertia 3, React 19).
kit:setupbrings it to the rest of the locked stack, whichstack.mdlists and the Architecture suite checks. - The starter kit without teams.
kit:setuprefuses a project made from its teams variant, before it changes anything: those migrations key team members on a bigintusers.id, which the kit keys on a uuid, and the team code does not follow the kit's rules. Build the teams a project needs with the kit instead.
Install
On a new project, five commands:
laravel new my-app --react # any test framework and package manager cd my-app composer require --dev playerarm123/laravel-workflow-kit php artisan kit:setup # asks which database: pgsql, mysql or mariadb php artisan migrate:fresh # once the database .env names exists php artisan test --testsuite=Architecture
kit:setup takes a few minutes, because it installs packages. Then finish the three things only you can do:
php artisan boost:install, and add"playerarm123/laravel-workflow-kit"to"packages"inboost.json, so your agent gets the guidelines and thelist-page/create-pageskills.npx playwright install chromium, for the Browser tests.- Decide who may read the audit log:
app/Policies/AuditEntryPolicy.phplets every user with a verified email in until you change it, andauditLogReader()/auditLogOutsider()intests/Pest.phpchange with it.
What kit:setup does
It changes only what is not in shape yet, so you can run it again at any time; a second run changes nothing. A file it cannot read the way it expects is left alone and listed under Left to do by hand.
| Step | What it changes |
|---|---|
| Composer packages | php to ^8.4; PHPUnit out, Pest 5 (with the Laravel and Browser plugins) in; Boost, Nightwatch, spatie/laravel-data and the s3 driver; then composer update |
| JavaScript packages | the @radix-ui/* packages for radix-ui (every import rewritten); ESLint, Prettier, Playwright, the table, date and diagram libraries; lint and format scripts; the starter kit's check and check:fix run Prettier and ESLint in place of vp check, whose formatter and linter disagree with them; the starter kit's single-package pnpm-workspace.yaml goes when you lock with npm or bun; then your package manager's install |
| Kit files | every file kit:install writes, plus files written once and yours from then on: the UUIDv7 IdGenerator and its provider, the InitializeUserContext middleware, AuditEntryPolicy, tests/Pest.php, eslint.config.js, .prettierrc, the SharedProps type, the main menu app/Http/Navigation.php, the four shadcn components the kit's pages use, tests for the starter kit's middleware, and an empty rule-overrides.json. The ExampleTests go. |
| Config | the database in .env.example and phpunit.xml, and in .env unless it is already on that engine (then only the keys it lacks); NIGHTWATCH_TOKEN; the Browser and Architecture suites; the PHPStan write-path rule; app.currency; the structure screen's Vite entry; the starter kit's .github/workflows/tests.yml, which gets a service on that engine, a step creating the testing database and one installing Playwright's Chromium, and runs on PHP 8.4 |
| Wiring | the providers, ExceptionResponses, InitializeUserContext, the props every page shares (locale, timezone, currency, translations, menu) in PHP and TypeScript, the sidebar and the header drawing the shared menu through useMainNavItems() and the kit's MainNavSidebar / MainNavHeader (a group shows as a folding heading in the sidebar and a dropdown in the header), a Navigation.php from before menu groups moved onto the BuildsMainMenu trait, and <FlashToast /> |
| Users on uuids | the users table, its sessions and passkeys, User, UserFactory and CreateNewUser (when registration is on) move to uuid keys. This edits the migrations in place, which is safe only before the first deploy. |
| Audit log page | its route, the // kit:routes marker, and every word the kit's pages speak in every lang/*.json and in the file of APP_LOCALE and APP_FALLBACK_LOCALE. A locale other than en gets the English words, listed for you to translate. |
Finally it runs Pint on the PHP files it edited, wayfinder:generate, lint (ESLint with --fix), your package manager's build, which the Feature tests of the kit's pages need, and kit:import. Under Next it lists only what is not done yet: the build, the migration, boost.json and Chromium drop out once they are.
--database=pgsql skips the question. --skip-dependencies edits composer.json and package.json but runs no install.
On an existing project
kit:setup is written for a project fresh from the starter kit. On an older one, commit first, run it, and read the diff: every edit is small and named in the table above, and what it cannot place is listed for you. The steps by hand are in the guidelines' Kit files sections, and each failing check names the one it wants.
When a check fails
Every failure reads [rule:check] subject: message — see rule.md in the workflow kit's guidelines. The ones a new project meets most:
| Failure | Fix |
|---|---|
[stack:major] x: is declared but missing from composer.lock (or the JS lockfile) |
run composer update or your package manager's install |
[stack:required] x: is required but not declared |
run php artisan kit:setup again, or require the package |
[stack:database] .env.example DB_CONNECTION: is "sqlite" |
php artisan kit:setup --database=pgsql |
[stack:ci-database] .github/workflows/x.yml: runs the tests without a postgres service |
add the engine's service to the job (stack.md, "Database"); kit:setup adds it to the starter kit's own workflow |
[stack:js-tools] package.json scripts.check: runs `vp check` … |
run prettier --check resources/ && eslint . there instead; kit:setup does it for the starter kit's script |
[stack:forbidden] cn or [stack:cn-import] … after npx shadcn add |
add components with php artisan kit:ui-add <name>, which imports cn from @/lib/utils and never installs the cn package |
[list-pages:menu] resources/js/pages/x/index.tsx: has no entry … |
add "menu" to the resource manifest and run php artisan kit:apply, or set "menu": null (list-pages.md) |
[list-pages:menu-group] x.index: sits in the group 'y', which … does not declare |
add $this->group('y', '{title key}', '{lucide icon}') to app/Http/Navigation.php where the group should sit (list-pages.md) |
[list-pages:menu-ability] x.index: asks y of Model, whose policy does not answer it |
add y() to the policy, or give the manifest's entry "ability" the ability the page's controller asks, and run php artisan kit:apply (list-pages.md) |
[…:kit-files] x: differs from the kit |
php artisan kit:install --force takes the kit's copy |
[testing:mirror] X: has no tests/…Test.php |
write that test at that path (testing.md) |
[structure:in-json] .kit/structure/X.json: is missing |
php artisan kit:import |
[audit-log:labels] … |
label the event, subject or key in every lang/*.json (audit-log.md) |
A rule that cannot hold in one place is recorded by the project's owner in rule-overrides.json (stack.md). An agent never adds one.
Quick usage
Design, then build
php artisan kit:import # write .kit/structure/*.json from the code as it stands # design: edit the JSON, or open /kit/structure locally php artisan kit:plan # the steps: done, ready, or waiting with the reason php artisan kit:apply # run every ready step: generators, binding and route lines
kit:apply stops where only a person can go on: a Command's fields, a Row's keys, a method's body. Fill those in and run it again. It picks up where it stopped.
The design screen has a step-by-step guide with screenshots: docs/structure-screen.md (also at /kit/docs/guides/structure-screen in the app). A domain service, from choosing one to its tests, has a guide of its own: docs/domain-services.md (/kit/docs/guides/domain-services).
To change something already built, mark the new piece with replaces on the screen or in the manifest. kit:apply builds it and swaps it in, then php artisan kit:retire removes the old one once the suites pass (structure.md).
Or scaffold one piece
A row action with its bulk twin (actions.md):
php artisan make:use-case CancelLotteryDraw --domain=LotteryDraw --command --repo=LotteryDraw
# fill in the Command: ids, plus the action's own fields; the handler returns int
php artisan make:action Cancel --model=LotteryDraw --domain=LotteryDraw
php artisan make:action Cancel --model=LotteryDraw --domain=LotteryDraw --bulk
A list page (list-queries.md, list-pages.md):
php artisan make:use-case ListLotteryTypes --domain=LotteryDefinition --command --result --query
# fill in the Criteria, the Row's toArray() and the adapter
php artisan make:controller LotteryType --domain=LotteryDefinition --only=index,destroy
php artisan make:list-page ListLotteryTypes --domain=LotteryDefinition
Each generator writes the test beside what it writes, with a ->todo() per case, and prints what it could not do: the route line, a missing policy ability, missing translation keys.
Commands
| Command | What it does |
|---|---|
kit:setup [--database=] [--skip-dependencies] |
Set up a project made from the React starter kit, in one run |
kit:ui-add {components*} [--overwrite=] [--allow=] [--dry-run] |
Add shadcn components the way the stack wants them, instead of npx shadcn add (stack.md) |
kit:install [--force] |
Write the kit's files into the project |
kit:import [--context=] [--resource=] [--force] |
Write the structure manifest from the code |
kit:plan [--context=] [--resource=] |
Show the steps that build the manifest |
kit:apply [--context=] [--resource=] |
Run those steps |
kit:retire [--context=] |
Remove what a finished replacement took over |
make:entity {name} --domain= [--child] |
An aggregate root or child entity, and its Unit test |
make:entity-method {entity} {method} --domain= [--param=] [--throws=] |
A behaviour or an assertion on an entity |
make:enum {name} --domain= --string --case= [--transition=] |
A domain enum, with its transitions when it is a status |
make:value-object {name} --domain= [--field=] |
A value object and its test |
make:domain-exception {name} --domain= --kind=refusal|value|application |
An exception on the right base |
make:domain-service {name} --domain= (--creates=|--data|--plain) [--repo=] [--exception] [--force] |
A domain service in one of its three shapes, with the repository it injects, its own exception and its test. Walkthrough: docs/domain-services.md |
make:port {name} --domain=|--application= [--adapter= --infra=] |
A port, and later its adapter and the adapter's test |
make:eloquent-repository {name} --domain= |
A repository, its exceptions, log payload and contract test |
make:use-case {name} --domain= [--command] [--result] [--repo=] [--creates] [--query] |
A use case: handler, Command, Result, and for a list its query port and adapter |
make:controller {name} --domain= --only= [--grid] |
Resource controller methods in the kit's shape, one test per action |
make:action {verb} --model= --domain= [--bulk] [--use-case=] |
An action's controller, request and test |
make:form-request {name} --domain= |
Store/Update requests and the form values |
make:form-page {name} |
The form component, create/edit pages and TypeScript type |
make:list-page {name} --domain= [--grid] [--types-only] |
A list page, its toolbar, TypeScript types and Browser test |
make:policy {model} |
A policy and its test |
Run php artisan help <command> for every option.
Checks
php artisan test --testsuite=Architecture # the rules, read off the code vendor/bin/phpstan analyse # write-path: only App\Infra writes to the database npx eslint . # the page rules: one table, one way to the server, dates, numbers
When a rule cannot hold in one place, the project's owner records an approved exception in rule-overrides.json (stack.md). An agent never adds one.
Updating the kit
composer update playerarm123/laravel-workflow-kit php artisan boost:update # new guidelines and skills php artisan kit:install # new kit files; it lists the ones that differ php artisan kit:install --force # take the kit's copy of those php artisan test --testsuite=Architecture
Contributing
The kit tests itself on a small Laravel app in workbench/, through Orchestra Testbench. You need PHP 8.4, Node 22 and a PostgreSQL database named workflow_kit_test on 127.0.0.1, user postgres.
composer install (cd workbench && npm ci) # ESLint and Prettier, for the generators' lint tests composer test # the Feature suite: generators and structure tooling composer lint # Pint composer analyse # PHPStan
A change to a rule changes its guideline in resources/boost/guidelines and its spec in tests/Architecture together. A change to a kit file changes its copy in resources/kit; run php vendor/bin/testbench kit:install --force to put it into the workbench.
The kit's CI also builds a project with laravel/react-starter-kit, installs the kit from the archive Packagist would serve, runs kit:setup and holds it to the starter kit's own composer ci:check: Prettier, ESLint, tsc, Pint, PHPStan and every suite, the Architecture and Browser ones among them (the starter-kit job). The Feature suite runs Prettier over the kit's own TypeScript with the .prettierrc kit:setup writes and the starter kit's stylesheet.
License
MIT. See LICENSE.
Read more
- Every guideline:
resources/boost/guidelines. On a local environment,/kit/docsrenders them with the console's commands. - The design screen:
/kit/structure, local only. How to use it: docs/structure-screen.md. The rules it follows: structure.md.