Laravel performance profiling
for AI coding agents.

Capture queries, detect performance issues or N+1 patterns, and run EXPLAIN ANALYZE — all via Artisan commands that output structured JSON to stdout. No browser, no GUI.

bash
# 1. install the package
$ composer require mateffy/laraperf --dev
 
# 2. start a 2-minute capture window
$ php artisan perf:watch --seconds=120
✓ session=session-20260416-143201-xK9mQp pid=47821 duration=120s
 
# 3. exercise the app, then analyse
$ php artisan perf:query
{ "n1_candidate_count": 3, "slowest_query_ms": 890, "total_queries": 183 }
 
# 4. drill into the query plan
$ php artisan perf:explain --hash=a1b2c3d4e5f6 | jq '.[0].Plan'
{ "Node Type": "Index Scan", "Actual Rows": 47 }

Designed for
how agents work

not for humans

Standard tools — Debugbar, Clockwork, Telescope — require a browser UI. LLM agents invoke commands, read JSON, and loop. laraperf is built around that model.

  • Every command exits immediately
  • All output is structured JSON to stdout
  • Status/errors go to stderr — safe to pipe
  • Hashes let agents reference queries across commands
  • Stack traces filtered to app/ — no vendor noise

Capture

Start a session and let it run. Captures every query automatically while your agent exercises the app.

Analyse

Get a JSON summary with total queries, slow queries, and N+1 candidates with source file and line number.

Explain

Run EXPLAIN ANALYZE on any query. Pass raw SQL or reference a hash from the query output.

Slow query detection

Find queries exceeding your threshold. Returns execution time, SQL hash, and exact source location in your codebase.

N+1 Detection

Identifies repeated queries that should be eager loaded. Groups identical SQL patterns and counts occurrences.

Clean stack traces

Your agent only sees your app code. Vendor frames are filtered out so the source location points directly to your code.

Multi-tenant

Override the database name at runtime. No config changes needed, works with any tenancy setup.

Pest integration

Write performance assertions directly in your test suite — query counts, N+1 detection, duration limits — with a fluent API.

How agents improve performance

Your agent runs commands, reads JSON output, and iterates. No browser UI, no human intervention.

  • Capture runs in background while agent exercises the app
  • Query returns structured data with file paths and line numbers
  • Explain shows query plans to diagnose performance
  • Agent fixes code and repeats until clean
1
Run perf:watch

Start a capture session in the background

2
Interact with your application

Open pages, trigger actions, run tests — whatever exercises your queries

3
Run perf:query

Find slow queries and N+1s in the captured session, with exact source file and line number

4
Run perf:explain

Investigate issues, find missing indexes, and understand query performance with EXPLAIN ANALYZE output

5
Fix and repeat

Your agent iterates — updating code, re-running captures, and improving until all issues are resolved

CLI reference

All output goes to stdout. Status lines go to stderr. Safe to pipe anywhere.

perf:watch

Start a capture session. Returns immediately by default (detached). Workers run in the background and append queries to a JSON file.

--syncRun in foreground; Ctrl+C or timeout ends it
--seconds=NWindow duration. Default: 300
--foreverKeep alive indefinitely (detached only)
--tag=labelLabel stored in session metadata
bash
$ php artisan perf:watch --seconds=120
✓ session=session-20260416-143201-xK9mQp pid=47821 duration=120s
Use `perf:stop` to stop early, or wait for the timeout.
Then run: php artisan perf:query --session=session-20260416-143201-xK9mQp

Built-in Pest testing

Write performance assertions directly in your test suite. Measure queries, memory, and N+1 patterns with a fluent API — no CLI needed.

Declarative constraints

Chain constraints onto any Pest test. They're validated automatically after the test runs — no manual assertions needed.

->maxQueryCount(10)Max allowed queries
->maxDuration(500)Max total duration in ms
->maxMemory('10M')Max memory usage
->maxN1Candidates(0)Max N+1 patterns
->noN1Patterns()Require zero N+1 issues
->maxQueryDuration(100)Max single query ms
tests/Performance/UserListTest.php
test('user list has no N+1 queries')
->maxQueryCount(10)
->noN1Patterns()
->maxDuration(500);
tests/Feature/DashboardTest.php
use function Mateffy\Laraperf\Testing\{measure};
 
test('dashboard loads fast', function () {
$result = measure(fn () =>
User::with('posts')->paginate()
);
 
expect($result->queryCount())
->toBeLessThan(20);
});
 
test('contact query performance', function () {
$result = measure(fn () =>
Contact::with('company')->get()
);
 
expect($result->durationMs())
->toBeLessThan(100);
});

measure()

Wrap any callback with measure() and get a full PerformanceResult — duration, memory, query count, N+1 candidates, and timeline events. Works in tests, tinker, or anywhere in your app.

durationMs()Total execution time in ms
peakMemoryHuman()Peak memory (e.g. "2.4 MB")
queryCount()Number of queries executed
n1Candidates(3)N+1 patterns detected
slowQueries(100)Queries above a threshold
summary()Quick overview array

Fluent expectation API

Chain assertions on duration, query count, N+1 detection, and more. Filter queries by table, operation, or connection before asserting.

->performance()->duration()Assert on total duration
->performance()->queries()->count()Assert on query count
->performance()->queries()->whereTable('users')Filter before asserting
->performance()->toHaveNoN1()Zero N+1 patterns
->performance()->toHaveNoSlowQueries(50)No queries above 50ms
->performance()->n1(5)Custom N+1 threshold
tests/Feature/ContactsTest.php
test('contacts page performance', function () {
$result = measure(fn () =>
Contact::with('company')->get()
);
 
expect($result)
->performance()->duration()
->toBeLessThan(100)
->performance()->queries()
->whereTable('contacts')->count()
->toBeLessThan(5)
->performance()
->toHaveNoN1()
->performance()
->toHaveNoSlowQueries(50);
});
tests/Feature/ImportTest.php
use function Mateffy\Laraperf\Testing\{capture, timeline_mark};
 
test('import progress tracking', function () {
$cap = capture();
timeline_mark('start');
 
$importer = new ContactImporter();
$importer->import($csv);
 
timeline_mark('imported');
 
$result = $cap->stop();
 
// Timeline marks let you measure phases
$importMs = $result->durationBetween('start', 'imported');
expect($importMs)->toBeLessThan(5000);
});

capture() & timeline marks

For finer control, start and stop capture manually. Drop timeline_mark() between phases to measure specific steps — then query the deltas with durationBetween() and memoryDelta().

capture()Start a manual capture session
timeline_mark('label')Mark a point in the timeline
$cap->stop()Stop and get PerformanceResult
durationBetween('a','b')Time between two marks
memoryDelta('a','b')Memory change between marks

Installation

Two ways to get started — manual install or let your agent handle it.

Manual install

1Install via Composer
composer require mateffy/laraperf
2Using Laravel Boost?

If you have Laravel Boost installed, run the following after installing laraperf — the skill is automatically added.

php artisan boost:update

Let your agent do it

Install the skill permanently with the CLI, or paste a prompt for a one-shot setup.

npx skills add mateffy/laraperf

Or paste this prompt for a quick one-shot:

Using Laravel Boost? Run php artisan boost:update after installing — the skill is added automatically.

Agent skill

laraperf ships a skill that teaches your agent the full capture-analyse-explain loop. Install it permanently or use it on-the-fly — one command either way.

What the skill teaches

The laraperf-profiling skill is a markdown document that lives in the repo. It contains the complete workflow: which commands to run, how to parse their JSON output, and how to iterate from detection to a fix.

1

Capture queries

Start perf:watch, exercise the code path, collect the session

2

Detect issues

Run perf:query to find N+1 patterns and slow queries with file:line sources

3

Analyse plans

Run perf:explain on flagged queries to find missing indexes or seq scans

4

Fix and verify

Apply the fix (eager load, index) and re-capture to confirm improvement

Recommended

Install via skills CLI

Permanently adds the skill to your project so every agent session has it. The skill is copied into your agent's skills directory and tracked in skills-lock.json.

npx skills add mateffy/laraperf
-gInstall globally for all projects
-a claude-codeTarget a specific agent
-ySkip confirmation prompts

Or paste a one-shot prompt

No install needed. Send this prompt to your agent — it will fetch the skill and set everything up.

Compatible agents

Claude Code

Full support via npx skills or prompt

Cursor

Install to .cursorrules or paste in composer

Codex / OpenAI

Add to AGENTS.md or paste as a task prompt

OpenCode

Install to .agents/skills/ via npx skills

Laravel Boost

Auto-installed — run php artisan boost:update after composer require

Any agent

Fetch skill.md directly — it's plain markdown, no auth needed