nunomaduro / pao
Agent-optimized output for PHP testing tools
Requires
- php: ^8.3
- ext-simplexml: *
- shipfastlabs/agent-detector: ^1.1.0
Requires (Dev)
- brianium/paratest: ^7.20
- laravel/pint: ^1.29.0
- pestphp/pest: ^4.4.3 || ^5.0.0
- pestphp/pest-plugin-type-coverage: ^4.0.3 || ^5.0.0
- phpstan/phpstan: ^2.1.45
- rector/rector: ^2.3.9
- symfony/process: ^7.4.5 || ^8.0.5
- symfony/var-dumper: ^7.4.6 || ^8.0.6
Conflicts
- pestphp/pest: <4.4.3 || >=6.0
- phpunit/phpunit: <12.5.14 || >=13.0.0 <13.0.5 || >=14.0
This package is auto-updated.
Last update: 2026-04-01 00:49:22 UTC
README
🚧 Work in progress — PAO is under active development. Want to try it early? Install from dev:
composer require nunomaduro/pao:dev-main --devThen just run your tests with any AI agent — the output will be JSON automatically. Feedback and bug reports are welcome at github.com/nunomaduro/pao/issues.
PAO is agent-optimized output for PHP testing tools. It works with any PHP project — Laravel, Symfony, vanilla PHP, or anything else that uses PHPUnit, Pest, or Paratest.
It detects when your tests are running inside an AI agent — Claude Code, Cursor, Devin, Gemini CLI, and others — and replaces the verbose, human-readable output with compact, super minimal, structured JSON. Zero config — just install and it works.
🔥 Benchmarks
PAO output is constant at ~20 tokens — no matter how large your test suite is.
🚀 1,000 Tests
| Runner | Without PAO | With PAO ⚡️ | Tokens Saved | Reduction |
|---|---|---|---|---|
| PHPUnit | 336 tokens | 20 tokens | 316 | 🟢 94% |
| Paratest | 351 tokens | 20 tokens | 331 | 🟢 94% |
| Pest | 10,123 tokens | 20 tokens | 10,103 | 🔥 99.8% |
| Pest --parallel | 11,125 tokens | 20 tokens | 11,105 | 🔥 99.8% |
💡 The bigger your test suite, the more you save. Pest goes from 11,125 → 20 tokens at 1,000 tests. That's 99.8% fewer tokens your AI agent needs to process!
💰 Cost Savings Per Session
A single run saves ~10K tokens with Pest. But in a real coding session you might run your test suite 20-50+ times. With 1,000 Pest tests and 50 runs, that's ~500K tokens saved:
| Model | 50 runs without PAO | 50 runs with PAO ⚡️ | Saved per session |
|---|---|---|---|
| Sonnet 4 | $1.52 | $0.003 | 🟢 $1.52 |
| Opus 4 | $7.58 | $0.015 | 🔥 $7.56 |
How this was calculated
- Token counts measured by running
vendor/bin/pestwith 1,002 tests (100 test files, 10 tests each + 2 feature tests) in a Laravel app, counting output characters and estimating ~4 characters per token - Cost per token: based on published input pricing as of March 2026 — Sonnet 4 at $3/MTok, Opus 4 at $15/MTok
- Assumes: test output counts as input tokens (agent reads the output). Does not account for output tokens, caching, or batch discounts
But the real win isn't cost — it's context window space. Every test run without PAO dumps 10K+ tokens of dots, checkmarks, and stack traces into your agent's context. After 50 runs, that's 500K tokens of test output competing with your actual code, conversation, and reasoning for the same limited context window. PAO keeps that to ~1K tokens total — freeing your agent to focus on what matters.
⚡️ Installation
Requires PHP 8.3+ — Works with PHPUnit 12-13, Pest 4-5, and Paratest.
composer require nunomaduro/pao --dev
That's it. PAO hooks into PHPUnit, Pest, and Paratest automatically through Composer's autoloader.
✨ Before & After
Your test suite with 1,000 tests goes from this:
PHPUnit 12.5.14 by Sebastian Bergmann and contributors.
............................................................. 61 / 1002 ( 6%)
............................................................. 122 / 1002 ( 12%)
...
.......................... 1002 / 1002 (100%)
Time: 00:00.321, Memory: 46.50 MB
OK (1002 tests, 1002 assertions)
To this:
{
"result": "passed",
"tests": 1002,
"passed": 1002,
"duration_ms": 321
}
🤯 That's up to 99.8% fewer AI tokens. The output is constant-size regardless of how many tests you have — and when tests fail, it includes file paths, line numbers, and failure messages.
Extra output from Pest plugins like --coverage or --profile is captured, cleaned of ANSI codes and decorations, and included as an output array in the JSON:
{
"result": "passed",
"tests": 1002,
"passed": 1002,
"duration_ms": 1520,
"output": [
"Http/Controllers/Controller 100.0%",
"Models/User 0.0%",
"Total: 33.3 %"
]
}
PAO was created by Nuno Maduro under the MIT license.
