andreapollastri / larapilot
Spec-driven AI product workflow for Laravel — discovery, backlog, planning, implementation, and review via Laravel Boost.
Requires
- php: ^8.1
- composer/semver: ^3.4
- illuminate/console: ^10.49.0|^11.45.3|^12.0|^13.0
- illuminate/contracts: ^10.49.0|^11.45.3|^12.0|^13.0
- illuminate/filesystem: ^10.49.0|^11.45.3|^12.0|^13.0
- illuminate/http: ^10.49.0|^11.45.3|^12.0|^13.0
- illuminate/routing: ^10.49.0|^11.45.3|^12.0|^13.0
- illuminate/support: ^10.49.0|^11.45.3|^12.0|^13.0
- laravel/boost: ^1.0|^2.0
- symfony/yaml: ^6.4|^7.0|^8.0
Requires (Dev)
- laravel/pint: ^1.20
- orchestra/testbench: ^8.36|^9.15|^10.6|^11.0
- pestphp/pest: ^2.36|^3.8|^4.1
- phpstan/phpstan: ^2.1
Suggests
- andreapollastri/checkpoint: Static Laravel security scanner. Install as --dev and set settings.security_scan=YES to fold `checkpoint:scan` findings into /larapilot-review and the pre-ship gate.
Provides
None
Conflicts
None
Replaces
None
- dev-main
- 5.0.3
- 5.0.2
- 5.0.1
- 5.0.0
- 4.1.4
- 4.1.3
- 4.1.2
- 4.1.1
- 4.1.0
- 4.0.2
- 4.0.1
- 4.0.0
- 3.2.3
- 3.2.2
- 3.2.1
- 3.2.0
- 3.1.2
- 3.1.1
- 3.1.0
- 3.0.4
- 3.0.3
- 3.0.2
- 3.0.1
- 3.0.0
- 2.7.2
- 2.7.1
- 2.7.0
- 2.6.0
- 2.5.1
- 2.5.0
- 2.4.2
- 2.4.1
- 2.4.0
- 2.3.2
- 2.3.1
- 2.3.0
- 2.2.0
- 2.1.2
- 2.1.1
- 2.1.0
- 2.0.0
- 1.9.1
- 1.9.0
- 1.8.2
- 1.8.1
- 1.8.0
- 1.7.3
- 1.7.2
- 1.7.1
- 1.7.0
- 1.6.1
- 1.6
- 1.5.1
- 1.5
- 1.4
- 1.3
- 1.2
- 1.1
- 1.0
- 0.3
- 0.2
- 0.1
- dev-copilot/fix-with-copilot
This package is auto-updated.
Last update: 2026-10-05 21:20:14 UTC
README
From product idea to reviewed Laravel code — with an AI product team that follows a real process.
Larapilot is a spec-driven workflow for Laravel projects, built on Laravel Boost. Install the package, run /larapilot-* skills in your AI editor, and get a PRD, a backlog, technical plans, and reviewed code — all versioned in .larapilot/ next to your app.
The agent proposes. You approve what ships. Human-in-the-loop, always.
📖 Documentation: larapilot.web.ap.it · Use cases · Custom skills · API
Contents
- Why Larapilot
- Quickstart
- The core loop
- Skills
- Custom skills — your own slash commands
- Workflow hooks — your commands on the loop
- What lands in
.larapilot/ - Developer domain docs
- Project settings
- Dashboard & API
- Economics
- Traceability
- Security
- Upgrades — Laravel, PHP, database
- Integrations
- Artisan CLI & MCP
- Requirements
- Learn more
Why Larapilot
AI agents are fast, but isolated prompts are not a product process. Larapilot gives your assistant a disciplined squad — discovery → backlog → plan → implement → review → ship — with 30 personas (Mark for product, John for architecture, Robert for review, Lars for security, Sarah for CLI/Git/Linux, Lucille for time and deadlines, …) used as review lenses, not costumes. Apps, websites, and PHP/Laravel Composer packages are all first-class.
Three layers, each with one job:
| Layer | Job |
|---|---|
Boost skills (/larapilot-*) |
Drive the conversation in your editor |
Artisan commands (larapilot:*) |
Persist state and enforce the workflow — skills never write workflow files by hand |
MCP server (larapilot) |
Lets the agent read backlog, specs, and diagnostics mid-conversation |
The workflow engine blocks invalid transitions: no implement before plan, no approve before review, no approve with open [blocks-merge] feedback or unfinished tasks (unless you pass --force).
Quickstart
composer require andreapollastri/larapilot --dev php artisan larapilot:install php artisan boost:install
larapilot:install scaffolds the .larapilot/ workspace, and also Larastan level 5+ and Laravel Pint (phpstan.neon.dist, pint.json, Composer scripts, dev dependencies). Run php artisan larapilot:quality before merge; larapilot:doctor fails when the gate is missing.
Already on Boost? Pick up the skills once with php artisan boost:update --discover.
If your editor does not list the MCP servers after boost:install, register them:
{
"mcpServers": {
"laravel-boost": { "command": "php", "args": ["artisan", "boost:mcp"] },
"larapilot": { "command": "php", "args": ["artisan", "mcp:start", "larapilot"] }
}
}
Then, in your editor:
| Your project | Type |
|---|---|
| A new idea — app, site, or Laravel package | /larapilot-inception "your product idea" |
| A Laravel app in production, built without Larapilot | /larapilot-adopt |
| A legacy system you are rewriting in Laravel | /larapilot-inception with a snapshot in .larapilot/legacy/ |
Upgrade
composer update andreapollastri/larapilot laravel/boost --with-dependencies php artisan larapilot:update php artisan larapilot:doctor
larapilot:update refreshes the runtime packs, task-templates.md, integrations.md, and the packaged design systems (--preserve-design-systems keeps yours), re-registers your custom skills, moves a handbook left in _project_docs/ by an older version into .larapilot/docs/handbook/, realigns the delivery forecast of a project that is already planned, then runs composer update laravel/boost (skipped inside a Composer script) and boost:update. Runtime-only refresh: php artisan larapilot:update --skip-boost. It never touches config.yaml (except to repoint an old paths.project_docs: _project_docs/), the PRD, plans, domain docs, or custom skills. In the backlog and the schedule it writes only what takes no decision — an id and a plain date on a milestone that lacks them, the release a milestone is named after, the deadline of an epic on the specs of that epic that lack it — and it says what the forecast now misses: dates that do not hold and inputs it could not read are for /larapilot-schedule. php artisan larapilot:schedule-apply --repair --dry-run lists those repairs without writing them. Do not re-run larapilot:install on an existing project unless you mean --force.
From 4.x to 5.0 — composer update stays inside the major your composer.json names, so raise the constraint first:
composer require andreapollastri/larapilot:^5.0 --dev --with-all-dependencies php artisan larapilot:update php artisan larapilot:doctor
If composer why andreapollastri/larapilot says requires rather than requires (for development), Larapilot sits in require: drop --dev, or Composer moves it to require-dev. Nothing in .larapilot/ is migrated: PRD, backlog, plans, decisions, and settings are read as they are. Three things to check: spec-list answers without the bodies (add --full where a script reads body); a custom skill that names a runtime part by its number should cite the heading, or start with larapilot:context {name} --with=…; /larapilot-boogle is /larapilot-error, and the boogle-* commands still answer. What v5 changes, measured: Version 5 vs version 4. Step by step — a branch, a dry run, the output to read, the checks as git grep commands, and the way back: Major releases.
The core loop
Greenfield — repeat steps 3–5 per user story:
/larapilot-inception "…" → /larapilot-spec → /larapilot-plan US-XXX
→ /larapilot-implement US-XXX → /larapilot-review US-XXX
Brownfield — adopt an app already in production:
php artisan larapilot:install → /larapilot-adopt → /larapilot-spec → (per-story loop)
| When | Start with |
|---|---|
| New product, site, app, PHP/Laravel package, pivot, or legacy rewrite | /larapilot-inception |
| Existing Laravel app in production, built without Larapilot, no PRD yet | /larapilot-adopt |
| One new capability on an existing product | /larapilot-feature "…" |
| Defect or regression | /larapilot-bug "…" |
| A request that may be either — a ticket, a client email | /larapilot-triage "…" |
| A change to the PRD that is neither — priorities, scope, a sharper requirement, a decision reversed, an older PRD brought up to date | /larapilot-prd "…" |
| Many planned stories at once | /larapilot-autopilot US-004 US-005 … |
| A team ritual the packaged skills don't cover | /larapilot-custom-skill |
Optional around the loop: /larapilot-design before plan · /larapilot-ship when the MVP stories are DONE · /larapilot-settings for project modes · /larapilot-usage for time and tokens · /larapilot-schedule to re-plan the order and the dates · /larapilot-economics for quotes and pricing.
Status machine: TODO → PLANNED → IN PROGRESS → REVIEW → DONE. A rejected review sends the story back to TODO with your feedback attached.
Git follows settings.git_mode (default GITFLOW, no auto-push): one feature/US-XXX-* branch per story, one atomic Conventional Commit per plan task; push and remote PRs only under GITFLOW_PUSH. With release_mode=YES, stories can branch from release/x.y.z instead of develop. Details: Git workflow.
Autopilot chains plan + implement for several specs, one at a time. Under STANDARD or MAX each spec runs in a fresh writing sub-agent; your session keeps the CLI transitions, questions, and the Robert/Lars review. Under ECO, or in an editor without sub-agents, it stays inline. You still run /larapilot-review per story unless auto_approve=YES.
Skills
Published by Laravel Boost after php artisan boost:install:
| Skill | Role |
|---|---|
/larapilot-inception |
Product discovery → PRD (includes Frontend Topology) |
/larapilot-adopt |
Reverse-engineer a PRD from an existing production codebase |
/larapilot-spec |
MoSCoW backlog from the PRD |
/larapilot-feature |
Mini-inception for one enhancement |
/larapilot-bug |
Bug triage → fix spec or rework. Reads the logs of the application every time, secrets redacted |
/larapilot-triage |
Bug or feature? Classifies a request against the PRD and the backlog, then hands off to /larapilot-bug or /larapilot-feature |
/larapilot-aikido |
Downloads the open security findings of Aikido, confirms them with you, groups them by fix, and hands each group to /larapilot-triage (aikido=YES) |
/larapilot-error |
Asks which tracker records the errors of production — Boogle, Sentry, Bugsnag, Flare, Datadog, Rollbar, Honeybadger, or CloudWatch — when none is set, downloads the open ones, confirms them with you, groups them by place in the code, and hands each group to /larapilot-triage (errors=YES) |
/larapilot-vendor-check |
Checks every dependency — Composer, the JavaScript of the repository, the frontend companion — against OSV.dev for known vulnerabilities, confirms each vulnerable package with you, then updates it, hands it to /larapilot-triage, or waives it with a reason |
/larapilot-laravel-upgrade |
Upgrades Laravel to the version you name: readiness report and criticalities first, then one major at a time on its own branch — dependencies, the upgrade guide, Filament, Nova, Livewire, Inertia — with an upgrade report |
/larapilot-php-upgrade |
Upgrades PHP: the suite on the target version, the packages that exclude it, deprecated code, every file that pins PHP (Docker, CI, Vapor, Herd, Sail), and the server runbook |
/larapilot-db-upgrade |
Upgrades or switches the database — MySQL 5.7 → 8.0 → 8.4, MySQL → MariaDB or PostgreSQL, a PostgreSQL major — with portable SQL, a local rehearsal on the target engine, and the data and cutover runbook |
/larapilot-prd |
Revises the PRD when it is neither — sharpen, re-scope, re-model, re-decide, upgrade — and aligns the stories that cite what changed |
/larapilot-design |
Static HTML mockups from a design system, with style variants to compare |
/larapilot-plan |
Technical plan + tasks for a spec |
/larapilot-implement |
Code + tests + developer domain docs, one commit per task |
/larapilot-review |
Human gate → DONE or rework |
/larapilot-autopilot |
Batch plan + implement, one fresh context per spec |
/larapilot-ship |
Security gate + deploy runbook when the MVP is done |
/larapilot-settings |
Persist project modes (effort, backlog, git, testing, account, auth, forges, notifications, …) |
/larapilot-release |
Semver release ledger + Gitflow release/x.y.z branches (release_mode=YES) |
/larapilot-project-docs |
Living handbook in .larapilot/docs/handbook/ (project_docs=YES) |
/larapilot-custom-skill |
Create your own skills under .larapilot/skills/, registered with Boost |
/larapilot-frontend-companion |
Link an external frontend repo or monorepo, name this product's projects, load its agent rules — driven from Laravel, or handed off to its team |
/larapilot-economics |
Aurora + Jennifer + Benjamin — quote, tax, payback, packaging, business plan, client quote (account ≠NONE) |
/larapilot-usage |
Lucille — time/token ledger, deadlines, Markdown report |
/larapilot-schedule |
Lucille — re-plan a project that is already planned: order, estimates, epic deadlines, and milestones against the forecast, with a dry run before anything is written |
/larapilot-backstage |
Publish the repo into a Backstage developer portal |
/larapilot-tracker |
Mirror the backlog into Linear · Asana · Jira · Trello · ClickUp · Monday |
Inception is a conversation, not a questionnaire. AskQuestion is used only for the fixed choices Larapilot persists; every answer gets a reaction before the next question; and before any requirement is written, Mark, Jennifer, and Benjamin run at least two challenge exchanges on the goal itself (who has this problem and what they do instead, how you will know in 90 days, the riskiest assumption, what would make you stop). Four rounds always happen, legacy rewrites included: Project Kind, Delivery Target, Business Model, and Operations & support. A skipped round is recorded as Not decided, never as a guess.
Inception verifies before it scopes, and writes a PRD a spec can be built from. After the goal challenge, Sebastian runs a prior-art check (prior_art, ON by default): he states the queries, asks consent, searches GitHub, Packagist, and open-source catalogs for products or packages that already do it, writes research/prior-art.md (license, stack, last release, what it covers and lacks, adoption cost), and you record a verdict — Build anyway, Adopt / fork, Integrate as dependency, or Not checked. Then the PRD gets its nouns and paths before its features: ## User Journeys (one per way a persona gets value — the unit a spec is cut from), ## Domain Model (entities, states, relations, glossary), every ### FR-XXX with a named actor and verifiable Done means bullets, ## Non-Functional Requirements with a target and a verifier per row, and ## Risks & Assumptions (riskiest assumption, kill condition, open questions with an owner, the Not decided list). Tom runs a ten-point Definition of Ready, Mark reads the decisions back in twelve lines, and only then is the PRD written. validate-prd reports the new sections as warnings, so older PRDs stay valid.
Changing the PRD after inception
The PRD changes through three doors, and each one leaves a row in ## PRD Revision History:
| The change is | Use | What it does to the PRD |
|---|---|---|
| One new capability, or a shipped one that must now behave differently | /larapilot-feature |
New FR-XXX, or the FR edited in place on a change request |
| A defect that shows a requirement was never written | /larapilot-bug |
A done-means bullet under the parent FR, or an NFR row. Never a "fix FR" |
| Anything else | /larapilot-prd |
See the revision kinds below |
| Revision kind | Example | Backlog impact |
|---|---|---|
| Editorial | Typos, a clearer sentence, a glossary term | None |
| Sharpen | Done-means for an FR, a target for an NFR, an open question answered | Criteria added to open stories |
| Re-scope | MoSCoW change, In Scope ↔ Future Phases, Delivery Target, an FR retired | Stories created, deferred, or deleted |
| Re-model | Entity renamed, state added, journey split | Stories citing the old names |
| Re-decide | Business model, ops owner, topology, data store, prior-art verdict | Architecture stories; quote recomputed |
| Upgrade | An older PRD gains journeys, domain model, NFRs, risks | None |
| Pivot | New vision or target user | /larapilot-inception, current PRD as input |
- Ids are permanent.
FR-,J-,NFR-, andQ-ids are never renumbered or reused. A dropped requirement keeps its heading withMoSCoW: Won'tand aRetiredline.validate-prdwarns onPRD_DUPLICATE_IDandPRD_DANGLING_REFERENCE. - The backlog follows.
php artisan larapilot:prd-impact --ids=FR-004,J-001lists the stories that cite the ids, with the action each needs:TODOre-issued,PLANNEDre-issued and planned again,IN PROGRESSon your consent,REVIEWsent back withspec-request-changes,DONEnever reopened. Without--idsit traces the whole PRD and lists the Must requirements no story covers. - Nothing is written before the readback. Mark shows before → after per changed item and asks Apply · Revise · Cancel.
- You edited
PRD.mdby hand? Run/larapilot-prd "I edited the PRD by hand". It reads the git diff and adds what a hand edit skips: the history row, validation, the inception snapshot, and the backlog check.
Context economy — what a skill loads
A skill opens with one command, php artisan larapilot:context {skill}. It answers the settings, the paths, what the PRD says about the project (kind, delivery target, budget, topology, …), and the runtime files that skill reads. Three things keep the context small, and keep an agent from applying a rule that is not the project's:
- Only what the skill needs. Every skill has its own list of packs. The heavy ones — tenancy, CI/CD, integrations, UX, deploy platforms — are named with the moment they apply and read only then.
- Only what the settings call for. The packs are compiled for the project into
.larapilot/cache/runtime/: underGITFLOWan agent reads theGITFLOWrules and never sees the other two modes; a toggle that is off has no section at all. Change a setting and the next call hands out the files that changed. - Only once per conversation. The call returns a session token. Passed back with
--session=, it tells the next skill of the same conversation which files are already loaded: the skill reads the new ones and nothing else. After the conversation is compacted,--freshreads everything again — a summary keeps the token and loses the rules.
| What is loaded (skill + runtime, default settings) | v4 | v5 |
|---|---|---|
/larapilot-implement, first skill of a conversation |
~43k tokens | ~15k |
/larapilot-review after implement, same conversation |
~38k | ~2k |
| triage → bug → plan → implement → review, one conversation | ~173k | ~31k |
Commands answer with the slice, too: spec-list is the backlog without the bodies (--full for everything), and prd-show reads the PRD by the piece — its outline, --ids=FR-004,J-001, or --section="Technical Architecture". A custom skill gets the same treatment: larapilot:context my-skill --with=delivery-1,dev-docs.
.larapilot/shared-runtime.md and the runtime-*.md files stay in the repository as the index for people, and as the fallback when the command cannot run. .larapilot/cache/ is derived, ignores itself in git, and is rebuilt whenever it is stale.
Custom skills — your own slash commands
The 29 packaged skills are the base layer. On top of them, each project can keep its own Boost skills in .larapilot/skills/ — committed with the code, registered with Boost automatically, built on the same larapilot:* CLI. Good candidates: a pre-deploy GO/NO-GO gate, a compliance check, client release notes, your team's house rules for a Filament resource.
Example — turn a pre-deploy checklist into /acme-predeploy-gate:
/larapilot-custom-skill "A pre-deploy gate we run before every production release:
nothing left in review, doctor healthy, quality green, security scan when it's on, then GO or NO-GO"
- Zoey interviews you in three rounds: one skill or a family · slash name and trigger description (the YAML
descriptionBoost routes on) · personas, runtime packs, and the CLI commands in order. - She shows the draft
SKILL.md— front matter, Shared Runtime, Team, Config & CLI, Workflow, Output Economy — the same shape as a packaged skill. - Sarah saves it with one command, which validates it, writes the canonical copy, mirrors it into
.ai/skills/and every agent skill folder that already exists (.claude/skills/,.cursor/skills/, …), and runsboost:update:
php artisan larapilot:custom-skill-add --name=acme-predeploy-gate --file=.larapilot/tmp-acme-predeploy-gate-SKILL.md
{
"schema": "larapilot/v1",
"kind": "custom_skill",
"data": {
"skill": {
"name": "acme-predeploy-gate",
"relative_path": ".larapilot/skills/acme-predeploy-gate/SKILL.md",
"registered": [".ai/skills/acme-predeploy-gate", ".claude/skills/acme-predeploy-gate"]
},
"boost_published": true
}
}
- Type
/acme-predeploy-gatebefore the next deploy. Commit.larapilot/skills/; teammates runphp artisan larapilot:custom-skill-listaftergit pullto register it on their machine.
The draft the example produces:
--- name: acme-predeploy-gate description: "Pre-deploy gate before a production release — GO or NO-GO. Use when someone says deploy, go live, release to production, or asks whether main is safe to ship." --- # Acme — Pre-deploy gate ## Context `php artisan larapilot:context acme-predeploy-gate` — read what `data.runtime.read` lists; `data.settings` is in that envelope. ## Workflow 1. `larapilot:spec-list --status=REVIEW` and `--status="IN PROGRESS"` — anything listed → NO-GO. 2. `larapilot:doctor` — `data.healthy` false → NO-GO. 3. `larapilot:quality` — an `E_QUALITY` error → NO-GO. 4. When `security_scan` is `YES`: `php artisan checkpoint:scan` — FAIL → NO-GO (or a logged waiver). 5. `larapilot:metrics` — one scope line; then GO / NO-GO, and `larapilot:notify` when notifications are on. Stop at the first NO-GO. Never deploy, push, tag, or merge from this skill.
| Rule | Behavior |
|---|---|
| Name | kebab-case, 1–63 chars; --name overrides the front matter |
| Front matter | description is required. A file with no front matter gets a placeholder one — replace it |
| Overwrite | refused without --force |
| Input | --file=, --content=, or stdin |
| Register again | custom-skill-list, larapilot:update, and the dashboard Skills page re-register every skill on disk |
| Remove | delete the folder from the dashboard File manager — it removes the mirrors in .ai/skills/ and the agent folders that still match the skill — then php artisan boost:update. By hand: delete .larapilot/skills/{name}/ and its mirrors. Registration never deletes a copy |
Full walkthrough: Your own skill · Contract: Custom skills.
Workflow hooks — your commands on the loop
hooks, OFF by default. A story always moves through the same transitions — planned, started, task done, review, approved or sent back, released, shipped. Hooks attach your team's own commands and skills to those moments, in .larapilot/hooks.yaml (committed, so the team shares them):
hooks: task.done: before: - name: Tests run: php artisan test --compact timeout: 900 spec.review: before: - name: Static analysis run: vendor/bin/phpstan analyse --no-progress - skill: acme-a11y-check spec.approved: after: - name: Deploy to staging run: curl -fsS -X POST "$FORGE_STAGING_DEPLOY_URL" ship: before: - skill: acme-predeploy-gate after: - name: Deploy to production run: php vendor/bin/envoy run deploy
| Event | Fired by | Event | Fired by |
|---|---|---|---|
prd.written |
prd-write |
spec.review |
spec-review |
spec.added |
spec-add |
spec.approved |
spec-approve |
spec.planned |
spec-plan |
spec.changes_requested |
spec-request-changes |
spec.started |
spec-start |
release.shipped |
release-ship |
task.done |
task-done |
ship |
/larapilot-ship: before as the gate starts, after on a GO |
| When | If it fails | |
|---|---|---|
before |
After the command's own checks, before anything is written | The transition is refused — E_PRECONDITION with details.hooks — and nothing is written. blocking: false only warns |
after |
Once the state is written | Reported under data.hooks.after.warnings; the transition stands |
run: |
Larapilot runs it from the project root, with the event in LARAPILOT_HOOK_* variables and as JSON on stdin |
The answer carries the last 40 lines; the whole output is in .larapilot/cache/hooks/ |
skill: |
The agent runs it. Before a transition, the command refuses until the agent reports it with --skill-hooks-done=name; after one, the answer lists it under data.hooks.after.skills |
— |
What teams use them for:
- Gates an agent cannot skip — tests, Larastan,
npm run build,composer audit, a coverage floor beforetask.doneorspec.review. An instruction in a prompt can be forgotten; a hook runs inside the command. - Deploys — staging after
spec.approved, production after a GO from/larapilot-shipor afterrelease.shipped. - Your rituals at the right moment — a custom skill as an architecture review after
spec.planned, an accessibility pass beforespec.review, client release notes afterrelease.shipped. - What Larapilot does not integrate — Teams, Mattermost, or email; a Toggl or Harvest timer on
spec.started; a Confluence or Notion export afterprd.written; an n8n, Zapier, or Make webhook.
php artisan larapilot:settings-set --hooks=YES
php artisan larapilot:hook-list # what is defined, and what is wrong with the file
php artisan larapilot:hook-run task.done --phase=before --spec=US-001 --task=TASK-01 --dry-run
- Nothing runs until you say so. Install writes
hooks.yamlwith every event listed and every example commented out, andhooksis OFF.LARAPILOT_HOOKS_ENABLED=falsein.envruns none on one machine — a CI runner, a teammate without the tools a hook calls. - A file with errors is a closed gate. While hooks are on, every transition is refused until
hook-listis clean or hooks are off;doctorreports it aschecks.hooks. - Artisan only. Hooks never run from the dashboard, the API, or MCP, where
hook-listis read-only./larapilot/settingsshows the hooks of the project. - The hooks are yours. Agents never edit a hook or turn hooks off to get past one unless you ask;
--forceonspec-reviewandspec-approveskips the task and feedback checks, never a hook. - Secrets stay in
.env— a hook reads them from its environment. Timeout 300 s by default (LARAPILOT_HOOKS_TIMEOUT), at most 3600 per hook.
What lands in .larapilot/
| Path | Purpose |
|---|---|
config.yaml |
Connector, paths, and project settings — committed, so the team shares one mode |
docs/PRD.md |
Product Requirements Document — the living product contract |
backlog.yaml · specs/US-XXX.yaml |
User stories with their status machine |
plans/US-XXX-plan.yaml |
Technical plans and tasks per spec |
docs/devs/ |
Developer domain docs — why the code is built the way it is (always on) |
docs/handbook/ |
Handbook — the living technical + functional manual of the project (project_docs=YES); only its README until then |
docs/review/ · test-results/ · security/ · launch/ · support/ |
Review findings, test evidence, OWASP assessments, launch checks, bug intake |
docs/quote.md |
Client quote in the PRD language (Economics) |
choices.yaml |
Snapshot of inception answers (kinds, targets, prior-art verdict, success signal, kill condition, ops) |
decisions.yaml |
Append-only journal of your explicit choices + regression guard (decision_log, ON) |
hooks.yaml |
Your commands and skills on the transitions of the loop — every example commented out until you write one (hooks, OFF) |
code-history.yaml |
Files and line ranges touched per spec/task (code_history, OFF) |
releases.yaml |
Semver release ledger (release_mode) |
tracker.yaml |
Spec → tracker issue ids — commit it, or every machine creates duplicates |
economics.yaml · economics.snapshot.yaml · economics.market.yaml |
Economics profile, last computed quote, researched competitors |
usage/ |
Lucille ledger (ledger.jsonl) and schedule |
mockups/{spec}/ |
Static HTML previews; styles/{slug}/ for style variants |
internal-feedback/{code}.md |
PM/dev comments until DONE |
skills/{name}/SKILL.md |
Your custom Boost skills |
client-materials/ · legacy/ · research/ · brand/ |
Inputs for inception, legacy snapshots, prior-art / reference-product / parity reports, brand assets |
design-systems/ |
Packaged references (Filament, Starter Kit, Bootstrap 5, Tailwind, AdminLTE) — add your own beside them |
shared-runtime.md · runtime-*.md · task-templates.md · integrations.md |
Rules and guides behind the skills — refreshed by larapilot:update, which also removes the runtime files a new version no longer ships |
cache/ |
The runtime compiled for the settings of the project, and what each conversation already loaded — derived, never committed (it ignores itself) |
auth.yaml |
Hashed dashboard users — git-ignored |
techdocs/ |
Generated Backstage TechDocs (after larapilot:backstage-export --write) |
Developer domain docs (docs/devs/, always on)
The one place where the reasoning behind the implementation survives the session that produced it. /larapilot-implement writes a Markdown file per domain / entity / feature — billing.md, user-authentication.md, webhook-ingestion.md — with a fixed skeleton:
| Section | What it carries |
|---|---|
Purpose |
What the domain is responsible for, in business terms |
Functional flow |
How it behaves step by step at runtime, failure paths included |
Technical design |
Models, services, actions, jobs, events, routes, commands, config keys, tests — and where they live |
Architectural choices |
What was chosen, what was rejected, and why |
Key decisions & invariants |
The rules that must stay true, and what breaks when they do not |
Extension points & gotchas |
Where to plug new behavior in, and the traps |
Three rules make it useful instead of decorative:
- Always English, whatever language the PRD and the conversation use — these files address whoever inherits the codebase.
- Updated in the same spec that changes the behavior, inside the task commit. A task is not
task-donewhile its domain doc describes the old code, and a domain whose code moved without its doc is a High review finding. - Never deferred. Domain docs survive
effort: ECO— the prose gets terse, the file still gets written.
There is no setting to turn them on: the folder ships with its contract (README.md) and skeleton (TEMPLATE.md), and /larapilot-ship blocks on a stale one. Path key: paths.dev_docs. Full contract: .larapilot/runtime-dev-docs.md.
A project with no docs is brought level on the first change, not gradually. config-show reports data.dev_docs.documented; when it is false and the codebase already has domains, the first spec, fix, or hotfix documents every existing domain, commits the backfill on its own (docs(US-XXX): bring developer domain docs level), and only then runs its own work. /larapilot-adopt does the same at the end of onboarding. Where the original reasoning is unrecoverable, the file says <!-- TODO: verify --> rather than inventing a motive.
This is not the handbook (docs/handbook/) — that optional manual is a mixed technical/functional manual for the whole project. docs/devs/ is engineering-only and mandatory.
Project settings
Set with /larapilot-settings or php artisan larapilot:settings-set --{setting}=VALUE (for example --effort=ECO); read with php artisan larapilot:config-show --only=settings.
| Group | Keys → default |
|---|---|
| Process | effort → STANDARD (ECO · MAX) · backlog → STANDARD (LEAN · GRANULAR) · git_mode → GITFLOW (NO_GITFLOW · GITFLOW_PUSH) · testing → NORMAL (MINIMAL · BEST) · auto_approve → NO |
| Tracking | lucille → YES (switched off by ECO unless you pass --lucille=YES) · decision_log → YES · code_history → NO |
| Discovery | prior_art → YES (Sebastian's existing-solutions search at inception, consent asked before every search) |
| Business | account → NONE (FREELANCE · COMPANY unlock Economics) |
| Delivery extras | release_mode → NO · project_docs → NO |
| Automation | hooks → NO (workflow hooks from .larapilot/hooks.yaml) |
| Access & security | comments → NO · dashboard_auth → NO · api_auth → NO · security_scan → NO |
| Integrations | github · gitlab · bitbucket · azure · notifications · notify_slack · notify_discord · notify_telegram → all NO |
php artisan larapilot:settings-set --effort=ECO --testing=MINIMAL php artisan larapilot:settings-set --release-mode=YES --comments=YES
ECO never spawns sub-agents and defers docs theater (README, PDFs, diagrams) — but still updates OpenAPI when an API changes, and still writes the developer domain docs. Playwright/E2E only runs under testing=BEST.
Two configuration layers
| Layer | File | Owns | Changed via |
|---|---|---|---|
| Laravel config | config/larapilot.php (publishable) + .env |
Environment toggles: routes, diagnostics, LARAPILOT_API_TOKEN, webhooks and tokens, Backstage and tracker credentials, package defaults |
php artisan vendor:publish --tag=larapilot-config, env vars |
| Project workflow | .larapilot/config.yaml (committed) |
Per-project settings, paths, statuses |
/larapilot-settings or larapilot:settings-set |
The YAML wins for workflow settings; Laravel config only provides the defaults at install. Machine-specific paths (like an external frontend repo) live in .env, never in committed YAML.
A few switches live in .env alone, because they decide what the package registers before any YAML is read: LARAPILOT_ENABLED=false keeps the package to its Artisan commands (no routes, no views, no MCP server, no recorders); LARAPILOT_DASHBOARD_ROUTE=false and LARAPILOT_MOCKUPS_ROUTE=false leave the dashboard and the mockups unregistered; LARAPILOT_API_AUDIT=false stops the audit log of the API (LARAPILOT_API_AUDIT_FILE moves it). Every other variable is documented beside its key in config/larapilot.php, and the ones of the integrations in .larapilot/integrations.md after install.
Dashboard & API (dev/staging)
Available when APP_ENV is local, development, testing, or staging — never in production. On staging the board, the PRD, the specs, the git history, the security findings, and the production errors are open to whoever reaches the host until dashboard_auth is YES: on a shared staging host, create a user and turn the sign-in on before anything else (Security). The mockups at /mockups follow the same sign-in.
| Page | URL | What you see |
|---|---|---|
| Board | /larapilot |
Kanban by status, with search and priority / epic / status filters; counts and metrics follow the cards on screen. Download status (.md) saves the board as it stands, filters included; Download epics (.md) saves the same stories as an outline — the project, each epic with its story points, its user stories with theirs, and the tasks of each with their hours |
| PRD | /larapilot/prd |
Rendered PRD with a search that looks in the PRD and nowhere else, decision journal timeline, Download PRD (.md), and a functional analysis summary download (one Markdown file in the PRD language, requirements numbered by priority) |
| Inception | /larapilot/inception |
Discovery choices snapshot |
| Plan | /larapilot/plan |
Epics, milestones, schedule criticality, and the delivery forecast: a dependency-aware Gantt with open work queued from today, one spec at a time. Re-planned with /larapilot-schedule. Download plan (.md) saves the epics, every story with its status, priority, points, release, blockers, and forecast window, the tasks of each planned story, the milestones, and the delivery order |
| Design | /larapilot/design |
Every screen first, as a card. A click opens that mockup as a site you browse, with All screens to come back; prev / next through every flow, style compare with Use this style, zip download |
| Settings | /larapilot/settings |
Every project mode with its options explained |
| Skills | /larapilot/skills |
Every skill the agents of the project can run, whoever brought it — the project, Larapilot, another package, Laravel Boost, a hand that dropped it into the folder of an agent — with which agent has it. Click one to read it. Under them, what the agents are told: CLAUDE.md, AGENTS.md, and the rules around them, one part for each author |
| File manager | /larapilot/files |
The five material folders — brand/, client-materials/, design-systems/, legacy/, skills/ — each with what it is for. Browse the tree, preview, read a PDF in the page, download, upload files or a whole folder (structure kept), rename, delete. A sixth folder, Project, shows the application itself, read only |
| Database | /larapilot/database |
The tables and views of the database in .env, whatever the driver — MySQL, MariaDB, PostgreSQL, SQLite, SQL Server. Rows a page at a time with sort, search, and foreign keys that lead to the row they point at; the structure of each table; a diagram of the tables and the foreign keys between them; the migrations that ran and the ones still to run; Download SQL for a dump of the whole database, and Other formats for its structure alone, for Laravel migrations, and for seeders with its rows. Read only; passwords and tokens are never shown |
| Logs | /larapilot/logs |
The log files of the application read as entries, the newest first: by level, by period, searched, or with the repeats counted — one row for each thing logged. An exception shows where it was thrown, and the frames of your own code apart from the framework's. Download log for the file. Read only; passwords, tokens, and keys are shown as [REDACTED] |
| Laravel | /larapilot/laravel |
How the framework is set up in the application and what it is doing, in five tabs. Overview: the drivers in use — database, cache, session, queue, mail, filesystem, broadcasting, logging — and what is cached: configuration, routes, events, compiled views, the application cache. Schedule: the scheduled tasks, the next one due first. Queue: the jobs that wait, are delayed, or are held by a worker, and the ones that failed. Mail: the mail the application sent, as it left. Dumps: what dump() and dd() printed, with the line that dumped it. Nothing of the application is changed |
| Git | /larapilot/git |
12-month contribution heatmap, every branch measured against the branch it is heading for, and the history drawn as a graph with each commit on the branch it was made on. Filterable by developer |
| Usage | /larapilot/usage |
Lucille's token and hour ledger + Markdown report, and Estimate vs build: every delivered spec with the hours it was estimated at, the time it spent IN PROGRESS, the wait in review, how many times it was sent back, and its tokens |
| Security | /larapilot/security · /larapilot/security/checkpoint |
Two tabs. Aikido: what Aikido found in the repository, the most severe first, with what was decided about each finding and the verdict of the ship gate. Register for the client (.md) downloads every finding — open, resolved, ignored with its reason. When the git remote matches no repository of Aikido, the page asks which one the project is. Always in the menu; with aikido off it says what Aikido is and how to connect it. Checkpoint: the last scan of andreapollastri/checkpoint — verdict, checks by area (dependencies, configuration, code), every finding with its suppression hash, the trend of the scans — with Run the scan and Download report (.md); when the package is missing it says how to install it |
| SBOM | /larapilot/sbom |
Every package the project ships — Composer, the JavaScript of the repository, and the frontend companion — from the lockfiles: version, direct or transitive, production or development, license (copyleft flagged), abandoned packages. Check vulnerabilities asks OSV.dev; the vulnerable packages come grouped with the version that fixes them and the command to run, and what was decided about each. Downloads: SBOM (.md), CycloneDX (.json), Vulnerabilities (.md) |
| Errors | /larapilot/errors |
What the running application threw, as the tracker of the project recorded it: one row for each bug, how many times it was thrown, and what was decided about it — day by day when the tracker records every throw. Always in the menu; with errors off it says what Boogle is, how to connect it, and which other trackers can be read instead |
| Economics | /larapilot/economics |
What the project costs, what the client pays, what is left for you — every sum written as a receipt (account ≠NONE) |
| Spec | /larapilot/specs/{code} |
Story, plan, tasks, mockups, decisions, internal feedback. Download spec (.md) saves all of it, tasks included, in one file |
| API docs | /larapilot/api/docs |
Swagger UI over the JSON API |
| Docs | /larapilot/docs |
Delivery loop, packaged skills, persona roster |
| About | /larapilot/about |
What the project runs on: Laravel, PHP, the database server, Node — each with its upstream support window drawn as a bar (bug fixes, security fixes, today) and an alert when it is past or near its end — the project, PHP extensions and limits, connections, drivers, the packages that shape an upgrade (Filament, Nova, Livewire, Inertia, …), frontend, CI and deploy, and every file that pins a version |
The dashboard follows the system theme; pin light or dark from the sidebar. Every page works on a phone.
File manager
/larapilot/files manages what you hand the skills before they start:
| Folder | What it is for |
|---|---|
brand/ |
Logo, palette, typography, and the brand guide |
client-materials/ |
Briefs, analyses, and documents supplied by the client |
design-systems/ |
Visual references and tokens the mockups are built on |
legacy/ |
Snapshots of the old system to port or migrate |
skills/ |
Your custom skills, one folder per slash command |
./ — Project |
The Laravel application itself: code, config, routes, tests. Read only |
- Adding, renaming, and deleting files and folders work in the five material folders. Project is read only: you browse, preview, and download; the routes that write do not know the folder and the service refuses a write to it.
- Folders that start with a dot are left out of Project —
.git,.larapilot,.github,.idea, at any depth — and so are the caches of the framework,bootstrap/cache/andstorage/framework/: a cached configuration holds every value of.env. Files that start with a dot are shown.vendor/andnode_modules/can be opened and are left out of the count. - Credentials show their keys, never their values.
.env,.env.*,auth.json,.npmrc,.netrc,.pgpassare shown with every value replaced by*****************, on screen and in the download; a template such as.env.exampleis shown as it is. A key or certificate is one line of asterisks; a database (.sqlite,.db) is listed and neither shown nor downloaded. A.logfile is shown and downloaded with the passwords, tokens, and keys it quotes replaced by[REDACTED], as on the Logs page — which reads a log whole, by entries, where the file manager shows its first 256 KB. - A PDF is read in the page: one page or two side by side, zoom, page jump, full screen. The reader is PDF.js from cdnjs, checked against its hash; where it cannot load, the file opens in its own tab.
- Upload a folder and it keeps its structure; an existing file is kept unless Replace existing files is on. One file is limited by
LARAPILOT_FILE_MANAGER_MAX_UPLOAD_KB(default 50 MB) and by PHP'supload_max_filesize/post_max_size, whichever is lower. - Local by default. The file manager is open in
local,development, andtesting. On any other environment (staging) it is served only whendashboard_authisYES— client documents and legacy snapshots stay behind a sign-in. - Nothing outside the six folders can be reached, a symlink is never followed, and an uploaded
.html,.js, or.svgis never run in the dashboard.LARAPILOT_FILE_MANAGER=falseremoves the page.
Database
/larapilot/database shows the application's own database — the connection named by DB_CONNECTION, with the DB_* values in .env — through Laravel's schema and query builders, so the page is the same on MySQL, MariaDB, PostgreSQL, SQLite, and SQL Server. It reads, and never writes.
- The list: every table and view, with its size where the driver reports one, and the driver, database, and host on top — never the password. On MySQL/MariaDB only the database in
.envis listed; on PostgreSQL every schema is, a table outsidepublicnamedschema.table. A connectionprefixis left out of the names. - Rows, 50 to a page (
LARAPILOT_DATABASE_VIEWER_PER_PAGE), by primary key. Click a column to sort; the search looks in the text columns, without regard to case; a foreign key value links to the row it points at. Click a row to open it whole — long text in full, JSON indented, Copy as JSON, and Copy as SQL INSERT: the row as oneINSERTstatement of the driver, its columns named, ready to run on a database with the same table. Binary is shown as hex with its size. - Structure: columns (type, null, default, primary key, auto increment, comment), indexes, foreign keys with their on update / on delete, and the create statement — the
CREATE TABLE(orCREATE VIEW) that builds it, with its indexes and keys, in the SQL of the driver, to copy. - Diagram, the second view of the list (
/larapilot/database?view=diagram): a box for each table with its columns —PK,FK, the type — and a line for each foreign key, from the column to the one it references, with an arrow on that end. A table stands to the right of the tables it points at; the ones no foreign key touches are in rows below. Keys only leaves the keys and counts the rest. Click a table to keep only its relations lit; its name opens it.−,+, Fit, Ctrl/⌘ with the wheel, and a drag move around it. Drawn on the server as plain SVG — no library, nothing fetched — for up to 300 tables and views; the structure alone, never a row. Download PDF saves the diagram as it is shown — all columns or keys only — on one page as large as the drawing, in vectors, with the database, the counts, and the day on top (/larapilot/database-diagram.pdf). - Migrations, the third view (
/larapilot/database?view=migrations): which migrations ran and which are still to run, asphp artisan migrate:statustells it — the files of the application and of its packages against themigrationstable. The pending ones come first, then the ones that ran with their batch, the newest first; a migration that ran from a file no longer there is marked Ran · file gone. Before the firstmigrateevery file is pending. Nothing is run from the dashboard. - Credentials are never shown. A column named like a password, token, or secret (
*password*,remember_token,token,*_token,*secret*,two_factor_recovery_codes,*api_key*,*private_key*) is shown as*****************and is never searched, sorted, or filtered on. Add patterns indatabase_viewer.masked_columnsofconfig/larapilot.php. - Download SQL writes the whole database as one
.sqlfile in the dialect of its driver — structure, every row, then indexes, keys, sequences, and views — to restore withmysql,psql -f,sqlite3, orsqlcmd. MySQL, MariaDB, and SQLite give their ownCREATEstatements; PostgreSQL is rebuilt from its catalogs likepg_dump(schemas, enum types, serial and identity columns with their next value); SQL Server from Laravel's schema builder. Each table and view is dropped first if it exists. It is written while it downloads, from one read-only snapshot. - The dump leaves credentials out: the hidden columns are written as
NULL, or''whereNULLis not allowed —hidden-1,hidden-2, … where a unique index holds the column, so the file still restores — and listed at the top of the file. Include passwords and tokens puts them in — offered only inlocal,development, andtesting. The same holds for the seeders and for Copy as SQL INSERT, which never carries them. - Other formats, beside Download SQL:
- SQL, structure only — the same file with no rows: the tables, their keys and indexes, and the views, and no sequence left at the number the rows had reached.
- Laravel migrations — a
.zipto unpack in the root of a project: a file indatabase/migrationsfor each table, written with the Blueprint method that makes each column (id(),timestamps(),softDeletes(),rememberToken()where a table carries them), its indexes, and its foreign keys. A table comes after the ones it points at; the keys that close a circle are in a last migration of their own, and the views in one that runs their SQL. What Blueprint has no word for — a type of one database only, an index on an expression — is written as the nearest thing, with a comment above it. Themigrationstable is left out. - Laravel seeders — a
.zipwith a class indatabase/seedersfor each table that holds rows, andDatabaseDataSeeder, which calls them in the order of the migrations:php artisan db:seed --class=DatabaseDataSeederon tables that are there and empty. Bytes are kept through base64, the next id is set on PostgreSQL, and the archive is written while it downloads, so a large table fills neither the memory nor the disk. - The migrations and the seeders are read from Laravel's schema builder, so they are the same from every driver, and what one database wrote runs on another — but for a view and a generated column, which are the SQL of the database they came from.
- The downloads are at
/larapilot/database-export/sql,/migrations, and/seeders— no extension, since a web server may keep.sqland.zipfor itself./larapilot/database.sqlstill answers. - Local by default, like the file manager: open in
local,development, andtesting; elsewhere served only whendashboard_authisYES.LARAPILOT_DATABASE_VIEWER_CONNECTIONreads another connection;LARAPILOT_DATABASE_VIEWER=falseremoves the page.
Logs
/larapilot/logs reads what the application wrote to storage/logs — every .log file, the one Laravel writes to now opened first, or the newest when it writes to none of them. It reads, and never writes. A file has its address without the extension — /larapilot/logs/laravel for laravel.log — because a web server may refuse an address that ends in .log, or look for a file of its own there; the address with it leads to the one without.
- Entries, not lines. An entry is what Laravel wrote between one
[date] env.LEVEL:and the next, its stack trace included; the newest comes first. Each one shows its level and message and, for an exception, the class, where it was thrown as a file of the project — a path of the server is read as the file it is in this checkout — the exception that caused it, and the context as indented JSON. - Your code apart from the framework's. A stack opens on the frames of the application; the ones of the framework and the packages are one click away. A file that is in the checkout opens in the file manager.
- By level, by period, by words. A level returns itself and every one more severe (Error and worse), and the count of each level sits above the list. The search wants every word, in any case; quotes keep a phrase together, and a class name is found with its backslashes as the page shows them or as the log doubles them. The period is the last hour, day, week, or month.
- Repeats counted turns the list into one row for each thing logged — the same message with its numbers, ids, and quoted values taken out, or the same exception thrown from the same line — with how many times and since when, the most repeated first. A log where nothing repeats is counted up to 10,000 different things, and the page says so.
- Any size. A file is read from its end backwards — 32 MB for a request (
LARAPILOT_LOG_VIEWER_SCAN_MB), 50 entries to a page (LARAPILOT_LOG_VIEWER_PER_PAGE) — so the newest entries of a log of gigabytes come at once; Older and Keep reading older go further back, and a page stays the same however much is written after it. A file that is not in Laravel's format, such as the output of a worker, is read a line at a time. - Secrets are never shown. Passwords, tokens, keys, cookies, and
Authorizationheaders are[REDACTED]on screen, and a search never finds one. In a context written as JSON the value under such a key is hidden whatever it is — a string, the list a header comes as, a number — and so is the JSON of a request body logged as text. A line the redaction cannot check is hidden whole. Download log gives the file redacted the same way, with a line longer than 1 MB cut at that size; Secrets as written gives it as it is, and is offered only inlocal,development, andtesting. - Local by default, like the file manager: open in
local,development, andtesting; elsewhere served only whendashboard_authisYES.LARAPILOT_LOG_VIEWER_PATHreads another folder — absolute, or from the root of the project;LARAPILOT_LOG_VIEWER=falseremoves the page. Only the.logfiles of that folder are opened, three folders deep, and a symlink is never followed. - For the skills,
php artisan larapilot:logsreads the same files — see Diagnostics and logs./larapilot-bugand/larapilot-errorrun it every time.
Laravel
/larapilot/laravel shows how the framework is set up in the application and what it is doing now. Five tabs; nothing of the application is changed from any of them — no cache is cleared, no job retried, no mail sent again.
- Overview. Five numbers that lead to the other tabs — scheduled tasks, jobs waiting, failed jobs, mail kept, dumps kept — then two panels. Drivers: for the database, the cache, the session, the queue, the mail, the filesystem, broadcasting, logging, and hashing (Scout and Octane when installed), the store, connection, or mailer in use, its driver, and what says where it points — host and port, bucket, table, prefix — never a password or a key. A driver that keeps nothing is said so: with
synca job runs inside the request, withloga mail reaches nobody. Caches: whether the configuration, the routes, and the events are cached, how many views are compiled, and the cache of the application — its store, whether it answers, and how much it holds where that can be counted (file,database) — each with the artisan command that builds it and the one that empties it. On a developer's machine a cached configuration carries a warning: a change to.envis not read until it is cleared. - Schedule. The tasks of the scheduler as
php artisan schedule:listprints them, read fromroutes/console.php,withSchedule(), or the console kernel: the command, the description, the cron expression, the next run, and its options — no overlap, one server, background, in maintenance, the environments it is limited to. A task that does not run in this environment is greyed. The page cannot tell whether the cron of the server callsschedule:run. - Queue. On the default connection — or another one of
config/queue.php, a click away — how many jobs wait, are delayed, or are held by a worker, queue by queue, and the first 50 jobs in the order a worker takes them: class, queue, state, attempts, since when. The jobs are listed for thedatabaseandredisdrivers — on Redis the queues are the one of the connection, the ones Horizon is told to work, and any other that holds a job; SQS, Beanstalkd, and the others give the numbers only. Failed jobs lists the last 50 with the first line of the exception, secrets redacted, and thequeue:retrycommand for each; open batches are counted. - Mail. Every mail the application sends is kept and listed, the newest first: subject, recipients, the mailable or notification that built it, the request or command it was sent during. A mail opens on its headers, the names and sizes of its attachments — not the files — and the message: the HTML inside a frame that runs no script, where a link opens in a new tab, and the plain text. It listens to what Laravel says it sent, so it works with every mailer,
logandarrayincluded, and changes nothing of the mail or of where it goes. - Dumps. What
dump()anddd()printed, the newest first: the value as text, the file and line that dumped it — the Blade template, for a dump in a view — and the request or command it happened in. A dump still shows where it always did; here it stays to be read, also when it came from a job, an API call, or a response nobody saw. While Laravel Herd is watching the dumps it takes them all, and the page says so. - Where they are kept. The last 100 mails and the last 100 dumps (
LARAPILOT_LARAVEL_VIEWER_KEEP) sit instorage/larapilot/, a folder that ignores itself in git (LARAPILOT_LARAVEL_VIEWER_PATHmoves it). Forget them all empties a list. They are kept on a developer's own machine only —localanddevelopment, and not while the tests of the project run: a mail carries reset links and personal data, a dump whatever the code was holding.LARAPILOT_LARAVEL_VIEWER_MAIL=trueorLARAPILOT_LARAVEL_VIEWER_DUMPS=truekeeps them wherever the page is served;falsenever. - Local by default, like the file manager: open in
local,development, andtesting; elsewhere served only whendashboard_authisYES.LARAPILOT_LARAVEL_VIEWER=falseremoves the page.
JSON API
| Endpoint | Returns |
|---|---|
GET /larapilot/api/board |
Metrics and specs grouped by status |
GET /larapilot/api/specs · /specs/{code} |
Specs with task progress, mockups, feedback (paginated, ?status=) |
POST /larapilot/api/specs/{code}/comments |
Append internal feedback (comments=YES) |
GET /larapilot/api/prd |
PRD Markdown and heading index |
GET /larapilot/api/metrics |
Delivery snapshot + effort timing + build: the estimates of the delivered specs beside the time they took |
GET /larapilot/api/economics |
Economics snapshot; what-if parameters (?hourly_rate=70&discount_pct=10&tier=premium) are computed, never stored |
GET /larapilot/api/diagnostics |
Read-only runtime snapshot for bug triage |
GET /larapilot/api/backstage · /backstage/catalog-info.yaml |
Backstage entities and delivery snapshot |
GET /larapilot/api/openapi.json · /docs |
OpenAPI 3 document and Swagger UI |
Rate limited per IP (LARAPILOT_API_RATE_LIMIT, default 120,1), mutating requests audited to .larapilot/api-audit.log, ETag / 304 on the read endpoints. Workflow state changes only through skills and Artisan — never from the dashboard or the API.
Economics (account, NONE by default)
settings.account is NONE | FREELANCE | COMPANY. Freelance uses sole-trader regimes (Italian forfettario, IRPEF, autónomo, …); company uses corporate tax plus dividend extraction (SRL, SPA, Ltd, GmbH, C-Corp, …), FY-2026 rates across 38 countries. Either unlocks /larapilot-economics and /larapilot/economics.
php artisan larapilot:settings-set --account=FREELANCE php artisan larapilot:economics-set --country=IT --regime=forfettario_15 --hourly-rate=55 php artisan larapilot:economics-set --product-model=saas --price-monthly=29 --churn=4
- The page is written for someone who has never read a balance sheet. The answer comes before the detail, no figure appears without the sum that produced it, and a word of finance appears only in the glossary.
- The short answer — one sentence and four figures: what the client pays, what you keep (and how much of every 100 of the price that is), the time to deliver, the upkeep each year.
- Two receipts, each under a bar drawn to scale — how the price is built (work + running costs + margin − discount = client price, + VAT = what the client pays) and where the money goes (client price − running costs − tax and contributions − accountant = what you keep).
- A subscription, one step at a time — what one customer leaves, what the product costs every month, how many customers it takes with the division written out, and a chart of when the money comes back that reads any month by pointer or keyboard and tells the same story in sentences. Three forecasts side by side, each with a verdict in words.
- Words used here — VAT, margin, net, fixed costs, churn, break-even, and the rest, each with the figure it has in this project.
- A console of dropdowns (rate, margin, discount, team size, regime, how it is sold, price line, scenario, …) recomputes everything server side. Nothing is written from the browser: a simulation prints the
economics-setcommand that would make it real. - Hours come from the backlog — planned task hours per spec, story points where no plan exists — and the snapshot refreshes itself whenever specs, plans, the PRD, or inception change.
- What the delivered work took — under the hours, one line for you alone: the delivered specs as the quote counts them, and the time the agent took to build them (the time they were
IN PROGRESS, pauses included). Your own hours on them are not in it. It is on the page and in the internal report, never in the client quote. Spec by spec: Estimate vs build on Usage. - Sold as
fixed·saas·ecommerce·packagechanges how payback is read, never the hours or the build price. Onautoit follows the Business Model answer from inception. - Market research from Jennifer and Benjamin (
larapilot:economics-market-write) is plotted, never invented; skipped on a one-off client delivery unless you ask. - The maintenance retainer is priced from the inception answers (delivery target, who runs the server, support window, ship method) from a 12% baseline, and the page shows the arithmetic.
- The client quote is written by
/larapilot-economicsin the PRD's own language (.larapilot/docs/quote.md, download at/larapilot/economics/quote.mdoreconomics-show --format=quote), with infrastructure and security chapters derived from the project's real settings. A built-in template coversen·it·es·fr·de·pt·nl·pluntil one is written.
Figures are planning estimates, not tax advice.
Traceability
Decision journal & regression guard (decision_log, ON by default)
Every explicit choice — a fixed-choice answer or a free-text directive like "the background must be orange" — is appended, with a timestamp, to .larapilot/decisions.yaml:
php artisan larapilot:decision-log --topic="background color" --value="orange" --source=chat --skill=larapilot-inception
Before a later phase records a different value for the same topic, it runs larapilot:decision-check --topic="background color" --value="red". If that contradicts an earlier decision, the skill asks you to confirm — "on 2026-05-01 you chose orange; confirm red supersedes it" — and re-logs with --supersedes=<id>. The file is never rewritten. Turn it off with --decision-log=NO.
Code change history (code_history, OFF by default)
php artisan larapilot:settings-set --code-history=YES # after each task-done, /larapilot-implement runs: php artisan larapilot:code-log --spec=US-014 --task=TASK-03 --skill=larapilot-implement php artisan larapilot:code-history --file=app/Models/Post.php # where has this file been worked on?
Security
| Gate | Default | Turn it on |
|---|---|---|
Dashboard auth — HTTP Basic Auth on the /larapilot UI |
OFF | larapilot:dashboard-user add andrea then --dashboard-auth=YES |
API token — bearer token or X-Larapilot-Token on /larapilot/api/* |
enforced when LARAPILOT_API_TOKEN is set |
set the env var |
API auth — token mandatory; fails closed (503) with no token configured |
OFF | --api-auth=YES |
Security scan — andreapollastri/checkpoint in review and pre-ship; the last scan on Security → Checkpoint |
OFF | composer require --dev andreapollastri/checkpoint then --security-scan=YES |
| Vulnerable dependencies — every package of the SBOM against OSV.dev, at the ship gate and on the SBOM page | on demand | nothing: php artisan larapilot:vendor-audit or /larapilot-vendor-check |
| Aikido — the findings of Aikido for the repository, in triage and at the ship gate | OFF | credentials in .env, then --aikido=YES |
| Production errors — what the running application throws, read from Boogle, Sentry, Bugsnag, Flare, Datadog, Rollbar, Honeybadger, or CloudWatch, in triage and on the dashboard | OFF | a credential that reads the tracker in .env, then --errors=YES --errors-provider=… |
- Dashboard credentials are argon2id/bcrypt hashes in
.larapilot/auth.yaml(git-ignored, no database, noUsermodel); failed sign-ins are rate-limited per IP (LARAPILOT_DASHBOARD_AUTH_MAX_ATTEMPTS, default 30/min) — behind a load balancer or a CDN, make sure the application trusts the proxy (TrustProxies), or every visitor shares one address. The dashboard gate never touches the API or MCP, and the API gate never touches the dashboard. - Turn the sign-in on before a shared staging host sees the dashboard. With
dashboard_authOFF,stagingserves the board, the PRD, the specs, the git history, the security findings, and the production errors to whoever reaches the host.larapilot:dashboard-user addand--dashboard-auth=YESclose it; the mockups at/mockupsand their assets sit behind the same sign-in and are served withX-Frame-Options: SAMEORIGIN,X-Content-Type-Options: nosniff, and aframe-ancestors 'self'policy. - Without a token, API reads stay open in the allowed environments but writes are refused outside local/development/testing.
- The dashboard file manager, database, logs, and Laravel pages are stricter than the rest of the UI: outside local/development/testing they answer
404— reads and writes alike — untildashboard_authisYES. Mail and dumps are recorded only inlocalanddevelopmentunlessLARAPILOT_LARAVEL_VIEWER_MAIL/_DUMPSsay otherwise. - With
security_scan=YES, review and ship runlarapilot:checkpoint-scan:FAILfindings block the review (fix them, or log a waiver withlarapilot:decision-log);WARNfindings become notes. Larapilot never bundles the scanner; with the setting off it runs only when someone asks — the command, or Run the scan on the dashboard. The result stays in.larapilot/cache/checkpoint/, out of git: the details can quote code.
Checkpoint and the SBOM
php artisan larapilot:checkpoint-scan --report # runs checkpoint:scan --json, keeps it for Security → Checkpoint php artisan larapilot:checkpoint-scan --only="Hardcoded Secrets" --gate # exit 1 when a check fails php artisan larapilot:sbom # inventories, totals, licenses, abandoned packages php artisan larapilot:sbom --write=both # docs/security/sbom.md and sbom.cdx.json (CycloneDX 1.5) php artisan larapilot:vendor-audit --report --gate # OSV.dev; exit 1 on an open advisory at --fail-on=high or above php artisan larapilot:vendor-link GHSA-xxxx-xxxx-xxxx --spec=US-012 # the spec that fixes it php artisan larapilot:vendor-link GHSA-xxxx-xxxx-xxxx --waive --reason="Never fed user input."
- The SBOM reads
composer.lock, the JavaScript lockfile of the repository (package-lock.json,pnpm-lock.yaml,yarn.lock,bun.lock), and the one of the frontend companion when it is linked — its own, or the workspace's in a monorepo. Nothing is installed or downloaded. - The vulnerability check sends the name and the version of each package — nothing else — to OSV.dev, the open database behind the GitHub advisories, FriendsOfPHP, and npm. No account, no key. Severity is the advisory's word, else its CVSS 3 score; each package gets the version that fixes all its advisories and the command that moves to it (
composer update …,npm update …, or a new constraint when the current one does not allow the fix). - Decisions — the spec that fixes an advisory, or a waiver with its reason — live in
.larapilot/vendor-audit.yamlwith the trend of the checks: commit it. The advisories are cached in.larapilot/cache/./larapilot-shiprunsvendor-audit --gate: an open advisory athighor above that was not waived stops the release; one in the backlog counts until the update is merged.
Aikido
Aikido scans the repository on its side — dependencies, code, secrets, infrastructure. Larapilot runs no scanner and installs nothing: it reads what Aikido found over the public REST API, brings it into the workflow, and tells Aikido what you decided about each finding.
LARAPILOT_AIKIDO_CLIENT_ID= LARAPILOT_AIKIDO_CLIENT_SECRET= LARAPILOT_AIKIDO_REGION=eu # eu · us · au · me LARAPILOT_AIKIDO_REPOSITORY= # id or name in Aikido, for this machine; empty = chosen or found from the git remote LARAPILOT_AIKIDO_FAIL_ON=high # critical · high · medium · low · none LARAPILOT_AIKIDO_PUSH_DECISIONS=true # false = decisions stay in the project
php artisan larapilot:settings-set --aikido=YES php artisan larapilot:aikido-status # setting, credentials, repository, last scan php artisan larapilot:aikido-issues --new --report # what nobody decided about; writes docs/security/aikido.md php artisan larapilot:aikido-plan --ids=24,31 # group confirmed ids by kind and fix before triage php artisan larapilot:aikido-link 24 --spec=US-012 # the spec that fixes it; leaves a note in Aikido php artisan larapilot:aikido-link 40 --waive --reason="Internal tool, never distributed." # ignores it in Aikido, with the reason php artisan larapilot:aikido-push # tell Aikido the decisions it was not told yet php artisan larapilot:aikido-repos # the repositories of the workspace; --use=12 says which one this project is php artisan larapilot:aikido-register # the register for the client: open, resolved, ignored with reason php artisan larapilot:aikido-issues --gate # exit 1 when the gate fails — for CI php artisan larapilot:aikido-scan # ask Aikido to scan again
- Create the credentials in Aikido under Settings → Integrations → Public REST API, with the
issues:readandrepositories:readscopes,issues:writeto tell Aikido your decisions, andrepositories:writeto ask for a scan. The repository has to be connected in Aikido, through the git provider. - The repository is asked for when it is not found. Larapilot finds it from the git remote, by address or by name. When the code is scanned under another repository — a fork, a mirror, a different name — it does not guess:
/larapilot-aikidoasks which one it is,/larapilot/securityshows the list with a form, andlarapilot:aikido-repos --use=12keeps the choice in.larapilot/aikido.yamlfor every machine.LARAPILOT_AIKIDO_REPOSITORYin.envnames it for one machine. /larapilot-aikidodownloads the open findings, lets you confirm each one (resolve, waive, or skip), runslarapilot:aikido-planon the ids you chose to fix, and hands each resolution group to/larapilot-triagewith an Aikido finding block — same kind and same fix together, secrets never merged. Triage measures it against the PRD like any request — a known vulnerability in shipped code is a bug, a requirement gap when no requirement names security — and/larapilot-bugwrites the fix spec. The link between finding and spec is recorded.- The ship gate stops on an open finding at
LARAPILOT_AIKIDO_FAIL_ONor above that was not waived. A finding in the backlog is not fixed: it counts until Aikido no longer reports it, after the fix is merged and scanned. - A waiver needs a reason, in a sentence, and only the user gives it.
- Decisions are told to Aikido. A waiver ignores the finding in Aikido, with the reason as its comment; a spec leaves a note on the finding;
--forgettakes a waiver back. A finding that is in several repositories of the workspace is ignored in this one only. When Aikido refuses — credentials withoutissues:write— or cannot be reached, the decision is kept andlarapilot:aikido-pushtells it later.--localkeeps one decision in the project,LARAPILOT_AIKIDO_PUSH_DECISIONS=falseall of them. - The register for the client.
larapilot:aikido-register, or Register for the client (.md) on/larapilot/security, gives one Markdown document with every finding of the repository: open with the fix that is planned, resolved with the date, ignored with the date and the reason, and a count by severity — what a client or an auditor asks for, in the language of the PRD. A finding ignored by hand in Aikido is listed too; its reason stays in Aikido, which does not give it back. - What is kept:
.larapilot/aikido.yamlholds the decisions — ids, the spec, the reason, whether Aikido was told — and the repository that was chosen, and is meant to be committed. The credentials stay in.env; the access token lives in the cache and is never written to a file of the project.
Production errors
Larapilot reads the errors the running application throws from one tracker and brings each bug into the workflow. settings.errors turns it on, and settings.errors_provider names the tracker. Run /larapilot-error: when no tracker is set, it asks which one — Boogle or any of the others — and turns the errors on.
| Provider | What is read | In .env |
Closed from Larapilot |
|---|---|---|---|
boogle (default) |
Every throw the self-hosted Boogle recorded, and the outages its uptime monitor found | LARAPILOT_BOOGLE_URL · LARAPILOT_BOOGLE_TOKEN |
Yes |
sentry |
The unresolved issues of a project | LARAPILOT_SENTRY_AUTH_TOKEN · LARAPILOT_SENTRY_ORGANIZATION · LARAPILOT_SENTRY_PROJECT |
Yes |
bugsnag |
The open errors of a project | LARAPILOT_BUGSNAG_AUTH_TOKEN · LARAPILOT_BUGSNAG_PROJECT_ID |
Yes |
flare |
The open errors of a Flare project | LARAPILOT_FLARE_TOKEN · LARAPILOT_FLARE_PROJECT_ID |
Yes |
datadog |
The open issues of Datadog Error Tracking — or the error logs, with LARAPILOT_DATADOG_SOURCE=logs |
LARAPILOT_DATADOG_API_KEY · LARAPILOT_DATADOG_APP_KEY |
Yes — not with logs |
rollbar |
The active items of a project | LARAPILOT_ROLLBAR_ACCESS_TOKEN |
Yes |
honeybadger |
The unresolved faults of a project | LARAPILOT_HONEYBADGER_AUTH_TOKEN · LARAPILOT_HONEYBADGER_PROJECT_ID |
Yes |
cloudwatch |
The error lines of an AWS CloudWatch log group, through the AWS CLI signed in on the machine | LARAPILOT_CLOUDWATCH_LOG_GROUP |
No |
Every credential is one that reads the tracker — never the key the application reports with (FLARE_KEY, BUGSNAG_API_KEY, ROLLBAR_TOKEN, HONEYBADGER_API_KEY). The optional variables of each tracker are in .larapilot/integrations.md → Production errors, and larapilot:errors-status names every one that is missing.
php artisan larapilot:settings-set --errors=YES --errors-provider=sentry php artisan larapilot:errors-status # setting, tracker, credentials, project, what is missing php artisan larapilot:errors-list --new --kind=error --report # what nobody decided about; writes docs/support/errors.md php artisan larapilot:errors-plan --codes=BUG12,BUG21 # group the confirmed codes before triage php artisan larapilot:errors-link BUG12 --spec=US-012 # the spec that fixes the bug that code belongs to php artisan larapilot:errors-link BUG21 --ignore --reason="The mail provider was down on its side." php artisan larapilot:errors-resolve BUG12 # once the fix is released: closes it in the tracker
The skill and the commands are the same for every tracker. The names they had when Boogle was the only one still answer — boogle-status, boogle-errors, boogle-plan, boogle-link, boogle-resolve — and the ledger keeps its name, .larapilot/boogle.yaml. --boogle=YES still works: it means --errors=YES --errors-provider=boogle, and a project that turned Boogle on before 4.1.3 has nothing to change.
Boogle is the self-hosted one — an exception tracker and uptime monitor the application sends to with andreapollastri/boogle-client:
LARAPILOT_BOOGLE_URL=https://boogle.example.com # empty = taken from BOOGLE_SERVER LARAPILOT_BOOGLE_TOKEN= # the token of an admin user of Boogle LARAPILOT_BOOGLE_PROJECT= # id or title; empty = found from BOOGLE_PROJECT_KEY, then APP_URL
- One entry for each bug. Boogle and logs keep a row for each time an exception is thrown: Larapilot puts together the rows that share the exception, the file, and the line, and says how many times and on how many routes. Sentry, Bugsnag, Flare, Rollbar, Honeybadger, and Datadog Error Tracking group by themselves: their grouping and their count are kept. A file of the server (
/home/forge/…/releases/…/app/Services/X.php) is read as the file of the repository it is. /larapilot-errordownloads the open errors, lets you confirm each bug — resolve, ignore with a reason, or skip — runslarapilot:errors-planon the codes you chose to fix, and hands each resolution group to/larapilot-triagewith a Production error block. The same exception in the same folder of the application goes together, so one spec fixes it; outages, errors in a package, and application code never merge./larapilot-bugthen writes the fix spec, with a test that throws the same exception before the fix.- A decision is about the bug, not about one time it was thrown: the next time it happens it is not handed over again.
.larapilot/boogle.yamlholds the decisions and is meant to be committed. - Back after the fix. An error closed with
errors-resolveand thrown again is shown as such, first in the list: the fix did not hold. - Personal data stays in the tracker. The user, the query string, and the payload of a request are never read into a file, a report, the cache, or the chat. Larapilot keeps the exception, the message with addresses and long secrets masked, the file and line, the method and the path — with ids and tokens in the path replaced by
{id}and{token}. - Writing to the tracker is asked for.
errors-resolveis the only command that writes there; it runs when you say so, is not allowed through the MCP tool, and refuses where nothing can be closed — CloudWatch, and the logs of Datadog. - On the dashboard,
/larapilot/errorsshows every bug with how many times it was thrown and what was decided. A tracker that records every throw also gets the chart of the last two weeks, day by day.
Diagnostics and logs (bug triage)
The logs, read for an agent. php artisan larapilot:logs reads the log files of the application as entries, with secrets redacted: the message, the exception and where it was thrown, and the frames of the application — not the sixty of the framework under them. /larapilot-bug runs it every time, whatever the report says, and /larapilot-error for every group it hands to triage.
| Option | What it does |
|---|---|
--group |
One row for each thing logged, with how many times and since when — the most repeated first |
--level=warning |
That level and every one more severe |
--search="…" |
Every word must be in the entry; quotes keep a phrase together. A redacted value is never found |
--since=7d |
Minutes, hours, or days back (30m, 1h, 7d), or a date |
--limit= |
20 by default, 100 at most |
--files · --file= |
The log files there are · another one than the file the application writes to now. A file that is not in Laravel's format has no levels and no dates: --level and --since are left out, and the answer says so |
It answers with what the file holds — the count of each level and the period — before the entries, and with an empty list when nothing was logged. Also through the MCP RunArtisanTool; on the dashboard the same reader is the Logs page.
The health of the runtime. A read-only snapshot — app info, health checks (storage_writable, cache, database, queue, log_file), and a log tail with secrets redacted — that never mutates workflow state. LARAPILOT_DIAGNOSTICS_ENABLED=false turns it off, and larapilot:logs with it.
| Surface | How |
|---|---|
| CLI | php artisan larapilot:diagnostics (--lines=, --no-logs) — no token needed |
| MCP | the diagnostics tool |
| API | GET /larapilot/api/diagnostics?lines=100&no_logs=1 — same gate as the rest of the API; 404 when LARAPILOT_DIAGNOSTICS_ENABLED=false |
Upgrades — Laravel, PHP, database
Three skills move the project to a new version, and all three start the same way: a readiness report, then the criticalities — blocker, high, medium, low, info — and nothing changes until you choose upgrade now, add it to the backlog, or stop at the report.
php artisan larapilot:stack # Laravel, PHP, database, packages, frontend, pins — with support windows php artisan larapilot:upgrade-check --laravel=13 --report php artisan larapilot:upgrade-check --php=8.4 # --php-from=8.2 when composer.json does not say php artisan larapilot:upgrade-check --db=pgsql:17 --db-from=mysql:8.0 php artisan larapilot:upgrade-check --laravel=13 --offline # the lock only, no Packagist
upgrade-checkreadscomposer.lockand asks Packagist which release of each direct dependency supports the target Laravel — followingself.versioninto the packages of the same vendor, so Filament is measured byfilament/support— on the PHP that Laravel needs. Each package gets a verdict:ok,update(fits the constraint),bump(a new constraint, often a major: read its guide),blocker(no release supports the target yet),abandoned,private(Nova, Spark, a Satis: Packagist cannot see it). It lists every file that pins a version (Dockerfile and compose images, CI matrices,vapor.yml,.php-version,.nvmrc,config.platform.php,phpstan.neon,rector.php), scans the code for what the target PHP deprecates and for SQL the target database does not speak, and writes the report to.larapilot/docs/upgrades/(paths.upgrades). Composer has the last word: the report names the--dry-runthat asks it.- Upgrade now runs on its own branch (by
git_mode), after a baseline of the suite: PHP first when the target Laravel needs it, one Laravel major at a time, the database last; each step is the Composer change, the upgrade guide of that version (read through BoostSearch Docs, never from memory), the package playbooks — Filament's upgrade script,livewire:upgrade, Inertia server and client together, Nova's guide — the gates (about,route:list,config:cache, tests, Pint, Larastan, the build), and one commit. The end is an upgrade report with the deploy runbook and the rollback. /larapilot-db-upgradenever touches a production or shared database: it makes the code portable, proves it on a local rehearsal against a scratch database, and writes the data move (pgloader for MySQL → PostgreSQL) and the cutover.- About on the dashboard shows the same facts: each version with its support window, and the command that checks the next upgrade.
Integrations
All optional, all OFF until configured. Credentials live in .env — never in .larapilot/. Setup guide: .larapilot/integrations.md.
Forges & notifications
github / gitlab / bitbucket / azure add PR/MR URLs through gh, glab, the Bitbucket Cloud API, or az / the Azure DevOps REST API — orthogonal to git_mode. Probe with larapilot:github-status (and gitlab-, bitbucket-, azure-status). notifications plus notify_slack / notify_discord / notify_telegram fan out task, spec, PR, schedule, and ship events through larapilot:notify (LARAPILOT_SLACK_WEBHOOK_URL, LARAPILOT_DISCORD_WEBHOOK_URL, LARAPILOT_TELEGRAM_BOT_TOKEN + _CHAT_ID).
Frontend companion — split repo
When Frontend Topology is API + external frontend, Laravel stays the only cockpit: PRD, backlog, plans, and every /larapilot-* command run in the backend workspace, and the frontend repo is a linked write target — a single app, or a monorepo shared with other products, in Angular, React / Next.js, Vue / Nuxt, or Svelte / SvelteKit.
php artisan larapilot:frontend-set --path=/absolute/path/to/fe-repo # writes LARAPILOT_FRONTEND_REPO_PATH to .env php artisan larapilot:frontend-scan # workspace, projects, agent rules, conventions, commands php artisan larapilot:frontend-set --project=portal --project=admin # the projects of this product in a monorepo php artisan larapilot:frontend-rules --file=apps/portal/src/app/orders/order-list.ts # the rules that govern a file php artisan larapilot:frontend-brief US-012 # handoff: the brief the frontend team builds from
- Workspaces — Nx (the graph of
nx graphwhen Nx is installed, cached until the workspace moves; the files otherwise, plugin-inferred targets included), Angular CLI, pnpm / yarn / npm / bun workspaces with Turborepo or Lerna, Rush, or one app. Each project comes with its type, tags, targets, stack and installed version, and what it depends on. - An app kept in its own repository and built inside a monorepo — a
project.jsonwith nonx.json, atsconfigthat extends a file two folders up: the scan finds the monorepo among the parent folders (orfrontend-set --workspace=/absolute/pathlinks it, in.env), runs the commands there, and commits in the app's own repository. An app that is a workspace of its own but sits inside a monorepo reads that monorepo's agent rules too. Every path of the scan is relative toroot; commands run inrun_in. - Target projects and write scope — in a monorepo the user names this product's projects. Libraries only they use are owned; a library another app also uses is shared and changes only when a task names it.
- The frontend team's rules —
AGENTS.mdat any depth,CLAUDE.mdwith its@imports,GEMINI.md, Cursor (.cursorrules,.cursor/rules/*.mdcwithglobs/alwaysApply), Copilot (copilot-instructions.md,*.instructions.mdwithapplyTo), Windsurf, Cline, Junie, Kiro, Amazon Q, Roo, JetBrains AI. The editor never loads them from the Laravel workspace, so implement reads them on purpose, and they win on code. - What the code already does — measured on the target projects: standalone or NgModule components,
@ifor*ngIf, signal inputs,inject(), zoneless,<script setup>, Pinia store style, Svelte runes, test naming, styling. Recent files of each kind are the models for new ones. - Commands and generators —
nx run portal:test,nx affected,ng test --watch=falsewith Karma headless (the launcher the karma config defines, orChromeHeadless),turbo run --filter,pnpm --filter, … with the package manager of the lockfile; the team's own Nx generators before the plugins'. A target the installed CLI can no longer run — the TSLint builder since Angular CLI 13, Protractor since 19, or one the installed package does not ship — is left out and named. A project with no spec, or a team whose generators skip them, is a question for the user. - Commits in the team's style — the scan reads the history: Conventional Commits or not, types, scopes, the language of the subjects, commitlint and hooks. A task's commit follows it with
{code} TASK-NNin the subject. Vendored packages (built code copied into the repository) are listed and never edited. - API client — orval, openapi-generator, ng-openapi-gen, hey-api, openapi-typescript, kubb, RTK Query codegen: regenerated from the product OpenAPI, never edited.
- Playbooks — Angular, React, Vue, and Svelte defaults by major version, for what the rules and the code leave open.
- Handoff —
frontend-set --mode=handoffwhen the frontend team builds in its own repository:frontend-briefwrites the story, the frontend tasks, the API operations they call, and the mockups to.larapilot/docs/frontend-briefs/.task-donefinds a frontend task's commit in the frontend repository.
UI tasks in a plan carry repo: frontend (and project: in a monorepo); implement writes under the env-resolved path and commits there, hooks on. Or run /larapilot-frontend-companion. Details: Frontend companion.
Project trackers — Linear, Asana, Jira, Trello, ClickUp, Monday
Mirrors the backlog into the tool the rest of the organisation already uses. .larapilot/ stays the source of truth — the tracker is a window, not a second workflow.
php artisan larapilot:tracker-status --ping # provider, status map, credentials check php artisan larapilot:tracker-push --dry-run # what would change, no API calls php artisan larapilot:tracker-push # backlog → tracker php artisan larapilot:tracker-pull # tracker → drift report (read-only) php artisan larapilot:tracker-pull --apply # write mapped statuses back
| Provider | Auth | Destination | Subtasks | Status maps to |
|---|---|---|---|---|
| Linear | personal API key | team key | sub-issues | workflow state |
| Jira (Cloud, REST v2) | email + API token | project key | subtasks | status, via a workflow transition |
| Asana | personal access token | project gid | subtasks | section |
| Trello | key + token | board id | checklist items | list |
| ClickUp | personal token | list id | subtasks | list status |
| Monday | API token | board id | subitems | status-column label |
Stories become issues titled US-XXX — Title; plan tasks become native subtasks. Push is authoritative; pull is a report and changes the backlog only with --apply. Pull never sets a spec to DONE (that stays a human review gate) and never changes spec text. Set LARAPILOT_TRACKER_ENABLED=true, LARAPILOT_TRACKER_PROVIDER=…, and the provider's credentials (LARAPILOT_LINEAR_API_KEY / _TEAM, LARAPILOT_JIRA_BASE_URL / _EMAIL / _API_TOKEN / _PROJECT, …); status maps live in config/larapilot.php. Or run /larapilot-tracker. Details: Project trackers.
Developer portal — Backstage
Publishes .larapilot/ into Backstage — one way; the workspace stays the source of truth.
php artisan larapilot:backstage-export # preview the bundle (writes nothing) php artisan larapilot:backstage-export --write # catalog-info.yaml + mkdocs.yml + .larapilot/techdocs/
catalog-info.yaml and mkdocs.yml are never overwritten without --force; TechDocs pages are regenerated. Set at least LARAPILOT_BACKSTAGE_OWNER (also _SYSTEM, _LIFECYCLE, _COMPONENT_TYPE, _BASE_URL). For a portal plugin, GET /larapilot/api/backstage returns entities and a lean delivery snapshot — call it through the Backstage backend proxy so the API token stays server-side. Or run /larapilot-backstage. Details: Backstage portal.
Artisan CLI & MCP
Skills call these for you — run them by hand for scripting, CI, or debugging. Every command prints one JSON envelope: {"schema":"larapilot/v1","kind":"…","data":{…}}, or "kind":"error" with a stable code (E_INVALID_INPUT → exit 2, E_CONNECTOR → 3, E_PRECONDITION / E_NOT_FOUND → 4).
| Area | Commands |
|---|---|
| Setup & health | install · update · doctor (--human) · context {skill} (--session=, --fresh, --with=) · config-show (--only=settings,paths,frontend,tracker,dev_docs,backstage,workflow,personas) · settings-set · quality (--fix) |
| Access | dashboard-user {list|add|remove} |
| Discovery | prd-write · validate-prd · prd-show (--ids=, --section=) · prd-impact (--ids=) · choices-set · frontend-set (--project=, --mode=) · frontend-scan (--project=, --full, --no-cli, --fresh) · frontend-rules (--file=) · frontend-brief |
| Backlog | spec-list (--status=, --full) · spec-add · spec-show (--task=, --fields=) · spec-next · spec-delete · validate-spec · spec-comment |
| Design | mockup-choose-style US-XXX --style= |
| Plan & build | validate-plan · spec-plan · spec-start · task-done · spec-review |
| Review | spec-approve (--force) · spec-request-changes (--include-feedback) |
| Traceability | decision-log · decision-check · code-log · code-history |
| Metrics & usage | metrics · usage-log · usage-report (--insights, --format=json|md|human) · schedule-set (--release= for a milestone of one release) |
| Schedule | schedule-show (--only=queue,epics,deadlines,releases,alerts,findings) · schedule-apply --file= (--dry-run) |
| Economics | economics-set · economics-show (--format=json|md|quote) · economics-market-write · economics-quote-write |
| Releases | release-list · release-add · release-set · release-cut · release-feature · release-sync · release-ship (--push) · release-import |
| Custom skills | custom-skill-list · custom-skill-add (--name=, --file= / --content= / stdin, --force) |
| Hooks | hook-list (--event=) · hook-run {event} (--phase=before|after, --spec=, --task=, --release=, --dry-run) · --skill-hooks-done= on every command that fires an event |
| Stack & upgrades | stack (--only=, --no-db) · upgrade-check (--laravel=, --php=, --php-from=, --db=, --db-from=, --offline, --report, --gate) |
| Dependencies | sbom (--full, --write=md|cyclonedx|both) · vendor-audit (--cached, --new, --limit=, --report, --fail-on=, --gate) · vendor-link (--spec=, --waive --reason=, --clear) · checkpoint-scan (--only=, --skip=, --cached, --report, --gate, --fail-on-warn) |
| Security | aikido-status · aikido-issues (--new, --severity=, --type=, --report, --gate) · aikido-plan (--ids=) · aikido-link (--spec=, --waive --reason=, --forget, --local) · aikido-push · aikido-repos (--search=, --use=, --forget) · aikido-register · aikido-scan |
| Errors | errors-status · errors-list (--new, --kind=error|outage, --limit=, --report) · errors-plan (--codes=) · errors-link (--spec=, --ignore --reason=, --forget) · errors-resolve (--status=FIXED|DONE, --comment=) — old names boogle-status · boogle-errors · boogle-plan · boogle-link · boogle-resolve |
| Integrations | github-status · gitlab-status · bitbucket-status · azure-status · notify · tracker-status · tracker-push · tracker-pull · backstage-export |
| Runtime | logs (--group, --level=, --search=, --since=, --limit=, --files, --file=) · diagnostics (--lines=, --no-logs) |
All commands are prefixed larapilot:. Release commands need release_mode=YES and take --semver= (Artisan reserves --version); nothing is pushed without --push.
The larapilot MCP server exposes four tools: BacklogListTool, SpecShowTool, DiagnosticsTool, and RunArtisanTool, which runs only read and validate commands (config-show, spec-list, spec-show, spec-next, metrics, usage-report, decision-check, code-history, prd-show, prd-impact, the forge probes, the three validators, doctor, diagnostics, logs, quality, frontend-scan, frontend-rules, backstage-export, tracker-status, hook-list, context, schedule-show, stack, upgrade-check, sbom, vendor-audit, aikido-status / aikido-issues / aikido-plan / aikido-repos, errors-status / errors-list / errors-plan, and their old names boogle-status / boogle-errors / boogle-plan). The parameters are checked too: each command takes through MCP only the ones that read, and an option that writes a file is refused — quality --fix, backstage-export --write / --force / --catalog= / --mkdocs= / --file=, usage-report --output=, aikido-issues --report, aikido-repos --use= / --forget, errors-list --report, upgrade-check --report, sbom --write=, vendor-audit --report. Run directly with Artisan, the commands take every option as before. All four tools are annotated as read-only (readOnlyHint).
Requirements
- PHP ^8.1 (8.2+ recommended)
- Laravel ^10.49 · ^11.45.3 · ^12 · ^13
- Laravel Boost ^1 or ^2 (Composer resolves Boost 1 on Laravel 10/11 and Boost 2 on Laravel 12+; kept current by
larapilot:update) - An MCP-capable editor (Claude Code, Cursor, VS Code, …)
On Laravel 10/11 the MCP stack pulls illuminate/json-schema — use a recent framework patch (Laravel 11.47+ recommended on 11.x).
Laravel 10 and 11 are past their security-fix window. Composer 2.9+ refuses every laravel/framework 10.x/11.x release because open advisories have no patched line (fixes shipped in Laravel 12.60+ / 13). Larapilot's CI still runs those majors by ignoring only laravel/framework advisories on the 10/11 jobs. Apps still on 10/11 that fail composer update with affected by security advisories need the same ignore (composer config --json policy.advisories.ignore '["laravel/framework"]') or should upgrade to Laravel 12+.
Learn more
- Version 5 vs version 4 — the context an agent loads, measured, and what to check when upgrading
- How it works — skills, artifacts, CLI, runtime packs
- Eleven use cases — new product, adopt an app, Laravel package, legacy porting, feature, bug, frontend companion, tracker sync, team server, SSH & tmux, your own skill
- Custom skills — anatomy, validation, lifecycle
- Developer domain docs
- Project settings
- Economics
- Design systems
- Team personas
- Changelog
License
MIT © Andrea Pollastri