# Maestro

> Maestro monorepo orchestrator for the official Laravel starter kits. Use this skill when making changes to any starter kit source files, building kits, running tests, or working with the kit inheritance hierarchy, placeholder system, teams feature, or any development workflow in this repo.

- Skill: `laravel/maestro` (Agent Skill)
- Install (CLI): `npx skillmds@latest add laravel/maestro`
- Raw SKILL.md: https://api.skillmd.com/api/skills/laravel/maestro/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Productivity
- Author: laravel (https://skillmd.com/u/laravel)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/laravel/maestro

---


# Maestro Skill

## What is Maestro

Maestro is the monorepo orchestrator for the official [Laravel starter kits](https://laravel.com/starter-kits). All starter kit source files live here and get built out to individual repositories. Changes are made in this repo, and Maestro automatically creates PRs for the affected starter kit repos after merge.

## Project Structure

```
maestro/
├── kits/              # Source files for all starter kits (inheritance-based layering)
├── orchestrator/      # Laravel app that builds and watches starter kits
├── build/             # Generated starter kit (git-ignored) — make changes here during dev
├── browser_tests/     # Cross-kit browser tests for CI
└── .github/           # GitHub workflows
```

## Commands

All commands below run from the `orchestrator/` directory unless noted otherwise.

| Command                                              | Description                                                                |
|------------------------------------------------------|----------------------------------------------------------------------------|
| `php artisan build`                                  | Build a starter kit into `build/`. Interactive or use flags (see below).   |
| `php artisan build --kit=vue`                        | Build Vue Fortify kit directly.                                            |
| `php artisan build --kit=svelte --blank`             | Build Blank Svelte kit.                                                    |
| `php artisan build --kit=livewire --workos`          | Build Livewire WorkOS kit.                                                 |
| `php artisan build --kit=react --teams`              | Build React Fortify kit with Teams.                                        |
| `php artisan build --kit=vue --workos --teams`       | Build Vue WorkOS kit with Teams.                                           |
| `composer kit:run`                                   | Start dev server + file watcher (syncs `build/` back to `kits/`).          |
| `composer kits:pint`                                 | Run Pint on `kits/` and `browser_tests/`.                                  |
| `composer kits:lint`                                 | Run `kits:pint`, then lint/format Inertia variants and sync back.          |
| `composer kits:check`                                | Build and run CI checks (`composer setup && composer ci:check`) for all 21 kit variants sequentially. |
| `composer kits:browser-tests`                        | Build and run browser tests for all 8 Fortify kit variants (4 base + 4 teams). |
| `php artisan fortify:matrix --kit=React`             | Run all Fortify `auth_features` permutations for one kit.                  |
| `npm run watch:kits`                                 | Run only the file watcher (no dev server).                                 |
| `composer setup && composer ci:check`                | Run inside `build/` — installs deps, builds frontend, runs checks (see below). |

### Selective Execution

Pass `--livewire`, `--react`, `--svelte`, and/or `--vue` to target specific frameworks.
Pass `--blank`, `--fortify`, `--workos`, `--components`, and/or `--teams` to target specific variants.
Combine both to narrow down exactly which kit variants to run:

```bash
composer kits:check -- --react --svelte
composer kits:check -- --vue --svelte --fortify     # Vue and Svelte, Fortify variants only
composer kits:check -- --livewire --fortify --workos # Livewire Fortify and WorkOS only
composer kits:check -- --workos                      # all frameworks, WorkOS variant only
composer kits:lint -- --vue
composer kits:lint -- --livewire                     # runs only the shared Pint step (no frontend lint phase)
composer kits:browser-tests -- --vue
```

No flags runs the full default matrix for each command.

Available `--kit` values defined in `orchestrator/app/Enums/StarterKit.php`.

### CI Checks (`composer ci:check`)

Every starter kit has a `ci:check` composer script that validates the kit without auto-fixing. Run `composer setup` first to install dependencies and build the frontend (`composer setup` runs `composer install`, `npm install`, and `npm run build`, which generates the Wayfinder types used by the frontend checks).

**Inertia kits** run: Vite+ formatting and linting via `npm run check`, framework type checking via `npm run types:check`, then `@test` (Pint + PHPUnit).

**Livewire kits** run: `@test` (pint + PHPUnit) only.

### Fortify Feature Matrix

Run from `orchestrator/`: `php artisan fortify:matrix --kit=React|Svelte|Vue|Livewire`.

Add `--teams` or Livewire-only `--components` as needed. The command builds `build/`, snapshots it, applies every `auth_features` permutation through `chisel.php`, and runs lint/checks for each result. Inertia matrix checks use Bun only; if Bun is missing, let it fail.

## Starter Kit Variants (21 total)

These are the full starter kit identifiers written to `orchestrator/storage/app/private/starter_kit` and used by `orchestrator/scripts/watch.js` for layer syncing.

| Stack     | Variant Identifier       | Auth / Feature Set        |
|-----------|--------------------------|---------------------------|
| Livewire  | `livewire-blank`         | Blank (no auth)           |
| Livewire  | `livewire`               | Fortify                   |
| Livewire  | `livewire-components`    | Fortify + Components      |
| Livewire  | `livewire-workos`        | WorkOS                    |
| Livewire  | `livewire-teams`         | Fortify + Teams           |
| Livewire  | `livewire-workos-teams`  | WorkOS + Teams            |
| React     | `react-blank`            | Blank (no auth)           |
| React     | `react`                  | Fortify                   |
| React     | `react-workos`           | WorkOS                    |
| React     | `react-teams`            | Fortify + Teams           |
| React     | `react-workos-teams`     | WorkOS + Teams            |
| Svelte    | `svelte-blank`           | Blank (no auth)           |
| Svelte    | `svelte`                 | Fortify                   |
| Svelte    | `svelte-workos`          | WorkOS                    |
| Svelte    | `svelte-teams`           | Fortify + Teams           |
| Svelte    | `svelte-workos-teams`    | WorkOS + Teams            |
| Vue       | `vue-blank`              | Blank (no auth)           |
| Vue       | `vue`                    | Fortify                   |
| Vue       | `vue-workos`             | WorkOS                    |
| Vue       | `vue-teams`              | Fortify + Teams           |
| Vue       | `vue-workos-teams`       | WorkOS + Teams            |

## Kit Inheritance Hierarchy

Starter kits are built by layering folders in priority order. Higher-priority layers override files from lower ones.

### Inertia (React / Svelte / Vue)

```
Shared/Blank
  → Inertia/Blank/Base
    → Inertia/Blank/{React|Svelte|Vue}
      → Shared/Base
        → Inertia/Base
          → Inertia/{React|Svelte|Vue}

For Fortify variant, add:
            → Shared/Fortify
              → Inertia/Fortify/Base
                → Inertia/Fortify/{React|Svelte|Vue}

For WorkOS variant, add:
            → Shared/WorkOS
              → Inertia/WorkOS/Base
                → Inertia/WorkOS/{React|Svelte|Vue}

For Teams (on top of Fortify or WorkOS), add:
                  → Shared/Teams/Base
                    → Shared/Teams/{Fortify|WorkOS}
                      → Inertia/Teams/Base
                        → Inertia/Teams/{React|Svelte|Vue}
                          → Inertia/Teams/{Fortify|WorkOS}/Base
                            → Inertia/Teams/{Fortify|WorkOS}/{React|Svelte|Vue}
```

### Livewire

```
Shared/Blank
  → Livewire/Blank
    → Shared/Base
      → Livewire/Base
        → Shared/Fortify → Livewire/Fortify [→ Livewire/Components]
        OR Shared/WorkOS → Livewire/WorkOS

For Teams (on top of Fortify or WorkOS), add:
          → Shared/Teams/Base
            → Shared/Teams/{Fortify|WorkOS}
              → Livewire/Teams/Base
                → Livewire/Teams/{Fortify|WorkOS}
```

### What This Means for Editing

- The watcher (`orchestrator/scripts/watch.js`) syncs changes from `build/` back to the **most specific layer** in `kits/`.
- If a file exists in both `Shared/Blank` and `Inertia/Fortify/Svelte`, editing it in `build/` syncs to `Inertia/Fortify/Svelte`.
- Files in `kits/Shared/` affect **all** kits. Files in `kits/Inertia/Svelte/` only affect Svelte.
- The watcher **restores placeholders** (e.g., `{{dashboard}}`) before syncing back — you don't need to worry about placeholders when editing in `build/`.
- Fortify kits use `chisel.php` plus `@chisel-*` markers to remove optional auth features. Keep markers wide enough that removed sections leave valid PHP/TS/Vue/Svelte/Blade.
- In kit Chisel scripts, prefer Chisel helpers (`$c->npm()->run(...)`, `$c->npm()->remove(...)`) over hardcoded package-manager shell calls; Chisel follows the generated kit's Node runtime.

## Kits Folder Structure

```
kits/
├── Shared/
│   ├── Blank/       # Foundation: config, migrations, artisan, phpunit.xml, .env.example
│   ├── Base/        # Factories, bootstrap, gitignore
│   ├── Fortify/     # Auth Actions, Concerns, Providers, config/fortify.php
│   ├── WorkOS/      # WorkOS routes, migrations, config
│   └── Teams/
│       ├── Base/        # Core teams: models, trait, actions, events, middleware, policies, rules, notifications, migrations, factories, config
│       ├── Fortify/     # Fortify-specific: CreateNewUser (creates personal team), LoginResponse, RegisterResponse, UserFactory
│       └── WorkOS/      # WorkOS-specific: CreatePersonalTeam listener, auth routes, UserFactory
│
├── Inertia/
│   ├── Blank/
│   │   ├── Base/          # Shared Inertia backend (controllers, middleware, routes)
│   │   ├── React/         # React blank resources
│   │   ├── Svelte/        # Svelte blank resources
│   │   └── Vue/           # Vue blank resources
│   ├── Base/              # Authenticated backend (settings controllers, middleware, tests)
│   ├── React/             # React authenticated frontend
│   ├── Svelte/            # Svelte authenticated frontend
│   ├── Vue/               # Vue authenticated frontend
│   ├── Fortify/
│   │   ├── Base/          # Fortify backend (auth controllers, providers, tests)
│   │   ├── React/         # React auth pages
│   │   ├── Svelte/        # Svelte auth pages
│   │   └── Vue/           # Vue auth pages
│   ├── WorkOS/
│   │   ├── Base/          # WorkOS backend
│   │   ├── React/         # React WorkOS pages
│   │   ├── Svelte/        # Svelte WorkOS pages
│   │   └── Vue/           # Vue WorkOS pages
│   └── Teams/
│       ├── Base/              # Inertia teams backend: controllers, requests, routes, middleware, tests
│       ├── React/             # React teams UI: components (TeamSwitcher, AppHeader, etc.), pages, types
│       ├── Svelte/            # Svelte teams UI: components (TeamSwitcher, AppHeader, etc.), pages, types
│       ├── Vue/               # Vue teams UI: components (TeamSwitcher, AppHeader, etc.), pages, types
│       ├── Fortify/
│       │   ├── Base/          # Fortify teams backend: User model, FortifyServiceProvider, routes, auth tests
│       │   ├── React/         # React Fortify teams: welcome page, settings layout
│       │   ├── Svelte/        # Svelte Fortify teams: welcome page, settings layout
│       │   └── Vue/           # Vue Fortify teams: welcome page, settings layout
│       └── WorkOS/
│           ├── Base/          # WorkOS teams backend: User model, routes
│           ├── React/         # React WorkOS teams: welcome page, settings layout
│           ├── Svelte/        # Svelte WorkOS teams: welcome page, settings layout
│           └── Vue/           # Vue WorkOS teams: welcome page, settings layout
│
└── Livewire/
    ├── Blank/
    ├── Base/
    ├── Fortify/
    ├── Components/        # Multi-file Blade components variant
    ├── WorkOS/
    └── Teams/
        ├── Base/              # Livewire teams: Blade views (header, sidebar, team-switcher, team pages), tests
        ├── Fortify/           # Livewire Fortify teams: User model, FortifyServiceProvider, settings layout, routes, auth tests
        └── WorkOS/            # Livewire WorkOS teams: User model, settings layout, routes
```

## Placeholder System

The build process replaces `{{placeholder}}` tokens in files with framework-specific values. The mapping lives in `orchestrator/scripts/ui-components.json`. For example:

- `{{dashboard}}` → `Dashboard` (Svelte/Vue) or `dashboard` (React)
- `{{auth_login}}` → `auth/Login` (Svelte/Vue) or `auth/login` (React)
- `{{teams_index}}` → `teams/Index` (Vue) or `teams/index` (React)
- `{{teams_edit}}` → `teams/Edit` (Vue) or `teams/edit` (React)

Svelte and Vue use PascalCase page names. React uses kebab-case.

## Teams Feature

Teams is an additive layer that can be enabled on top of Fortify or WorkOS auth variants. It is **not** available with `--blank` or `--components` variants.

### Build Flag

Use `--teams` with any kit + auth variant combination:

```bash
php artisan build --kit=react --teams           # React + Fortify + Teams
php artisan build --kit=vue --workos --teams     # Vue + WorkOS + Teams
php artisan build --kit=livewire --teams         # Livewire + Fortify + Teams
```

In interactive mode, you'll be prompted "Would you like to enable the Teams feature?" after selecting an auth variant (unless Blank is selected).

### Supported Variants

| Stack       | Fortify + Teams  | WorkOS + Teams          |
|-------------|------------------|-------------------------|
| React       | `react-teams`    | `react-workos-teams`    |
| Vue         | `vue-teams`      | `vue-workos-teams`      |
| Livewire    | `livewire-teams` | `livewire-workos-teams` |
| Svelte      | `svelte-teams`   | `svelte-workos-teams`   |

These identifiers are written to `orchestrator/storage/app/private/starter_kit` and used by the watch script to determine which kit layers to sync.

### Architecture Overview

**Core (Shared/Teams/Base):** Models (`Team`, `Membership`, `TeamInvitation`), `HasTeams` trait (added to User model), `TeamRole` enum (Owner/Admin/Member with permission-based access), `CreateTeam` action, 9 domain events, 3 notifications (invitation, acceptance, removal), `TeamPolicy`, `EnsureMembership` and `SetTeamUrlDefaults` middleware, validation rules, migrations, factories, and `config/teams.php`.

**Auth variant overrides:**
- **Fortify** (`Shared/Teams/Fortify`): Overrides `CreateNewUser` to create a personal team on registration. Custom `LoginResponse`/`RegisterResponse`/`TwoFactorLoginResponse` that resolve the current team and set URL defaults before redirecting.
- **WorkOS** (`Shared/Teams/WorkOS`): Uses a `CreatePersonalTeam` event listener on Laravel's `Registered` event. Overrides auth routes to include team context after authentication.

**Inertia controllers** (`Inertia/Teams/Base`): `TeamController` (CRUD + switch), `TeamMemberController` (role update, removal), `TeamInvitationController` (create, cancel, accept). Routes defined in `routes/teams.php` with `EnsureMembership` middleware.

**Frontend components** (per framework in `Inertia/Teams/{React|Svelte|Vue}`): TeamSwitcher, CreateTeamModal, AppHeader, AppSidebar, NavUser, UserInfo, Dashboard, teams/index page, teams/edit page, TypeScript types.

**Livewire views** (`Livewire/Teams/Base`): Blade components for team-switcher, header, sidebar, desktop-user-menu. Team pages: index, edit, accept-invitation.

### Roles and Permissions

| Role   | Level | Permissions                                                                                              |
|--------|-------|----------------------------------------------------------------------------------------------------------|
| Owner  | 3     | team:update, team:delete, member:add, member:update, member:remove, invitation:create, invitation:cancel |
| Admin  | 2     | team:update, invitation:create, invitation:cancel                                                        |
| Member | 1     | (none)                                                                                                   |

Only Admin and Member roles are assignable. Owner is automatically set on team creation.

### Key Team Files

| File                                                      | Purpose                                                |
|-----------------------------------------------------------|--------------------------------------------------------|
| `kits/Shared/Teams/Base/app/Concerns/HasTeams.php`        | User trait: team relationships, switching, permissions |
| `kits/Shared/Teams/Base/app/Models/Team.php`              | Team model with slug routing, soft deletes             |
| `kits/Shared/Teams/Base/app/Enums/TeamRole.php`           | Role enum with hierarchical permissions                |
| `kits/Shared/Teams/Base/app/Actions/Teams/CreateTeam.php` | Team creation action (transaction, events)             |
| `kits/Shared/Teams/Base/config/teams.php`                 | Team config: user model, invitation settings           |
| `kits/Inertia/Teams/Base/app/Http/Controllers/Teams/`     | Team, member, and invitation controllers               |
| `kits/Inertia/Teams/Base/routes/teams.php`                | Team routes with EnsureMembership middleware           |

## Workflow

1. **Build**: `cd orchestrator && php artisan build --kit=svelte`
2. **Develop**: `composer kit:run` (starts dev server at localhost:8000 + watcher)
3. **Edit**: If `build/` exists and the watcher is running, make changes in `build/` — the watcher syncs them to `kits/`. Otherwise, edit `kits/` directly.
4. **Stop `kit:run`**: Press Ctrl+C before running `kits:lint` or `kits:check` — running them while the watcher is active corrupts `kits/`.
5. **Test**: `composer kits:pint` → `composer kits:lint` (Inertia only) → `composer kits:check`
6. **Commit**: Commit the changes in `kits/` (not `build/`)
7. **PR**: Create PR; after merge, Maestro auto-creates PRs for affected kit repos

## Browser Tests (Local CI Parity)

To run browser tests for all kits locally, run from the `orchestrator/` directory:

```bash
composer kits:browser-tests
```

This builds each variant (4 Fortify + 4 Fortify Teams), copies the appropriate browser tests, installs dependencies, and runs the tests — matching the steps in `.github/workflows/browser-tests.yml`.

Browser tests are split into three layers under `browser_tests/`:

- `browser_tests/bootstrap/` — shared Pest config, TestCase, and phpunit.xml. Copied for all 8 jobs.
- `browser_tests/common/` — Fortify browser tests. Copied only for non-teams builds.
- `browser_tests/teams/` — Teams browser tests. Copied only for Teams builds.

Each variant runs exactly one test suite (common or teams), not both.

### Per-Kit Command Sequence

Run from the repo root unless noted:

```bash
# 1) Install orchestrator dependencies
cd orchestrator
composer install --no-interaction --prefer-dist

# 2) Build the target kit (replace <Kit> with Livewire, React, Svelte, or Vue)
#    Add --teams for Teams variants
php artisan build --kit=<Kit>
cd ..

# 3) Copy browser test bootstrap + suite into build/
cp -r browser_tests/bootstrap/* build/
cp -r browser_tests/common/* build/      # For non-teams variants
# cp -r browser_tests/teams/* build/     # For Teams variants instead

# 4) Install browser testing dependencies in build/
cd build
composer remove --dev phpunit/phpunit --no-interaction --no-update
composer require --dev pestphp/pest pestphp/pest-plugin-browser pestphp/pest-plugin-laravel --no-interaction
npm install
npm install playwright@1.61.1  # Pinned: Playwright 1.62 removed server-side timeouts that pest-plugin-browser relies on

# 5) Install Playwright browsers/deps
npx playwright install --with-deps

# 6) Prepare app env and build frontend assets
cp .env.example .env
php artisan key:generate
npm run build

# 7) Run browser tests
php vendor/bin/pest --parallel
cd ..
```

## Key Files Reference

| File                                                 | Purpose                                                         |
|------------------------------------------------------|-----------------------------------------------------------------|
| `orchestrator/app/Console/Commands/BuildCommand.php` | Build orchestration logic                                       |
| `orchestrator/app/Enums/StarterKit.php`              | Available kit enum                                              |
| `orchestrator/scripts/watch.js`                      | File watcher: syncs build → kits with placeholder restoration   |
| `orchestrator/scripts/run.js`                        | Dev server launcher                                             |
| `orchestrator/scripts/ui-components.json`            | Placeholder → framework-specific name mapping                   |
| `orchestrator/storage/app/private/starter_kit`       | Stores which kit is currently built                             |
| `orchestrator/CLAUDE.md`                             | Laravel Boost guidelines (PHP, Laravel 12, Pest 4, Tailwind v4) |

## Important Rules

1. **Where to edit**: If a `build/` folder exists at the project root **and** `composer kit:run` is running (dev server + file watcher), make changes in `build/` — the watcher syncs them back to `kits/`. If there is no `build/` folder or the watcher is not running, edit `kits/` directly. Always commit in `kits/` regardless.
2. **Follow sibling patterns**: When creating a Svelte file, check the React and Vue equivalents for expected structure and behavior and vice-versa.
3. **Layer awareness**: Know which layer a file belongs to. Shared files affect all kits. Framework-specific files only affect that framework. Teams layers sit on top of auth layers — place team-specific code in the appropriate `Teams/` directory.
4. **Placeholder awareness**: Files in `kits/` contain `{{placeholders}}`. Files in `build/` have resolved values. The watcher handles conversion.
5. **Lint/check while watcher is stopped**: `kits:lint` and `kits:check` both delete and rebuild `build/` for each variant. If `kit:run` is active, the watcher interprets those deletions as file removals and propagates them to `kits/`, corrupting the source. Always stop `kit:run` before running `kits:lint` or `kits:check`. `kits:pint` is safe to run at any time — it operates directly on `kits/` and never touches `build/`.
6. **Lint changes**: Run `composer kits:pint` for fast PHP formatting of `kits/` and `browser_tests/`. Run `composer kits:lint` to run Pint then lint/format all Inertia variants and sync changes back to `kits/`.
7. **Test after changes**: Run `composer setup && composer ci:check` inside `build/` to verify nothing is broken.
8. **Teams layer placement**: When modifying teams code, choose the correct layer: `Shared/Teams/Base` for cross-stack logic (models, traits, middleware), `Shared/Teams/{Fortify|WorkOS}` for auth-variant-specific overrides, `Inertia/Teams/Base` or `Livewire/Teams/Base` for stack-specific backend, and `Inertia/Teams/{React|Svelte|Vue}` or `Livewire/Teams/{Fortify|WorkOS}` for frontend/variant-specific UI.

