nunomaduro/pao
Laravel PAO provides agent-optimized output for PHP tools. When it detects AI agents (Claude Code, Cursor, Devin, Gemini CLI), it replaces verbose CLI output from PHPUnit/Pest/Paratest/PHPStan/Rector/Artisan with minimal structured JSON—zero config, no impact on human terminal runs.
Installation:
composer require laravel/pao --dev
First Use Case:
Run your existing test suite or tool (e.g., phpunit, pest, phpstan) in an AI agent environment (e.g., Claude Code, Cursor, Gemini CLI). PAO automatically detects the agent and converts verbose output to structured JSON.
Example:
phpunit
Output (in AI agent):
{
"tool": "phpunit",
"result": "passed",
"tests": 1002,
"passed": 1002,
"duration_ms": 321
}
Laravel-Specific:
For Artisan commands (e.g., php artisan migrate:status), PAO strips ANSI colors and excess whitespace by default in AI environments. No additional configuration is needed.
Test a CI Pipeline: Run your test suite in a CI environment where an AI agent might process logs (e.g., GitHub Actions with Claude Code integration). Verify the output is now structured JSON, reducing log noise and token usage.
Debug Artisan Commands:
Use php artisan about or php artisan db:show in an AI environment. Observe the cleaned output (e.g., 75% fewer tokens) while retaining all critical information.
Zero-Configuration Integration:
phpunit.xml, pest.php, or other config files.Tool-Specific Workflows:
{
"tool": "pest",
"result": "failed",
"tests": 10,
"failed": 2,
"failures": [
{
"file": "tests/Unit/UserTest.php",
"line": 20,
"message": "Failed assertion: User::find(1) should return User."
}
]
}
{
"tool": "phpstan",
"result": "failed",
"errors": 2,
"error_details": {
"app/Http/Controllers/UserController.php": [
{
"line": 15,
"message": "Method UserController::store() should return void but returns string."
}
]
}
}
{
"tool": "rector",
"result": "success",
"totals": {
"changed_files": 1,
"errors": 0
},
"file_diffs": [
{
"file": "app/Models/User.php",
"diff": "--- Original\n+++ New\n@@ -1 +1 @@\n-public $name;\n+public string $name;"
}
]
}
php artisan about
Output (before PAO):
Environment ................................................................
Application Name ................................................... Laravel
Output (after PAO):
Environment ..
Application Name .. Laravel
Conditional Activation: PAO only activates in AI agent environments. Human-readable output remains unchanged in local terminals or CI pipelines without AI integration.
AI-Assisted Development:
result: "failed" from PAO’s JSON output.CI/CD Optimization:
- name: Run tests with PAO
run: phpunit
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Triggers PAO in AI agents
Local Development:
Laravel-Specific:
migrate:status, db:show, and about in AI environments to verify cleaned output.Custom Tools:
Pao\Contracts\Tool interface. See Pao’s source for details.Environment Variables:
PAO_ENABLED (if set to false, it disables agent detection). Useful for CI pipelines where AI agents are not involved.
PAO_ENABLED=false phpunit # Disables PAO
Debugging:
PAO_DEBUG=true to log agent detection and output transformations.
PAO_DEBUG=true pest
Agent Detection:
X-Agent header or sets PAO_AGENT=true.Boolean Environment Variables:
PAO_ENABLED=true vs. PAO_ENABLED=1).PAO_ENABLED=true or PAO_ENABLED=false explicitly. Updated in v1.1.1.Runtime Exits:
Artisan Command Output:
tinker) may not be fully supported or may include unsanitized output.Parallel Testing:
--parallel mode or Paratest may leak raw output (e.g., dots or progress bars) in some versions.Verify Agent Detection:
PAO_DEBUG=true to see if PAO detects your AI agent:
PAO_DEBUG=true pest
[PAO] Detected agent: cursor/1.0
Check Output Format:
tool field (added in v1.0.2). Example:
{
"tool": "phpunit", // Critical for parsing
"result": "passed",
...
}
Handle Missing Fields:
failures or error_details are missing in PHPUnit/PHPStan output, ensure your tool version is supported (e.g., PHPUnit 12-13, PHPStan 1.0How can I help you explore Laravel packages today?