ทั้งสองแพ็กเกจมาจากทีม Laravel ช่วงเดียวกัน และคิดเรื่องเดียวกัน คือผลลัพธ์ของเครื่องมือควรอ่านง่ายทั้งสำหรับคนและสำหรับ AI agent ที่เปิดโปรเจกต์ขึ้นมาทำงาน
Laravel Doctor ตรวจว่าเครื่องนี้พร้อมรันแอป
clone โปรเจกต์มาใหม่ หรือเพื่อนบอกว่า "เครื่องผมเปิดไม่ขึ้น" เรามักเริ่มไล่ทีละอย่าง มี .env ไหม ตั้ง APP_KEY หรือยัง ต่อฐานข้อมูลได้ไหม storage เขียนได้ไหม Laravel Doctor รวมรายการที่ต้องไล่นั้นไว้ในคำสั่งเดียว
composer require laravel/doctor --dev
php artisan doctor
เวอร์ชัน 0.1 ที่เราใช้มี diagnostic 27 ตัว แบ่งเป็นกลุ่ม
- Environment มี
.envไหม ตั้งAPP_KEYหรือยัง PHP ตรงกับที่composer.jsonต้องการไหม extension ครบไหม timezone ถูกต้องไหม - Composer ติดตั้ง dependency แล้วหรือยัง
composer.lockตรงกับcomposer.jsonไหม - Configuration โหลดและ cache config ได้ไหม ค่าที่ driver ต้องใช้ตั้งครบไหม
- Database ต่อได้ไหม มี migration ค้างไหม
- Cache, queue, session ต่อ driver ที่ตั้งไว้ได้ไหม และเตือนถ้า queue เป็น
syncบน production - Storage เขียนได้ไหม มี
storage:linkไหม - Security เปิด debug บน production อยู่ไหม
.envอยู่ใน.gitignoreไหม และรันcomposer auditให้ด้วย
ผลมีหกระดับ pass, notice, warn, fail, skip และ error คำสั่งจะจบด้วย exit code ที่ไม่ใช่ศูนย์เมื่อมี fail หรือ error ถ้าอยากให้ warn ทำให้ล้มด้วยก็ใส่ --fail-on=warn ลองรันบน repo ของเว็บนี้ Doctor เจอ migration ค้างสามตัว กับแพ็กเกจที่มีช่องโหว่หนึ่งตัวจาก composer audit
⚠ Composer audit passes
Composer audit found vulnerable dependencies.
1 security advisory was reported.
Suggested fix
Update or replace the vulnerable packages reported by `composer audit`.
Doctor ถามก่อนแก้ และแก้เฉพาะเรื่องที่แก้ได้แน่นอน
บางเรื่อง Doctor แก้ให้ได้ เช่น สร้าง .env จาก .env.example, สร้าง APP_KEY, เพิ่ม .env เข้า .gitignore, สร้าง storage link หรือรัน migration ที่ค้าง แต่จะถามก่อนทุกครั้ง
Migrations are current: Database migrations are pending.
Apply pending migrations using `php artisan migrate`? (yes/no) [yes]
การแก้ส่วนใหญ่จำกัดไว้เฉพาะ environment แบบ local Doctor แบ่งแอปเป็นสองโหมด local กับ production ตามชื่อ APP_ENV ถ้าชื่อ environment ไม่ใช่ local, production หรือ staging และไม่ได้ตั้งไว้ใน config ของ Doctor จะนับเป็น production ไว้ก่อน บน production Doctor จึงรายงานอย่างเดียว ไม่ migrate และไม่สร้าง key ให้
โหมดยังเปลี่ยนคำตัดสินด้วย อย่าง config ที่ยังไม่ cache บนเครื่อง dev ถือว่าปกติ แต่บน production แปลว่า deploy ไม่ได้รัน php artisan optimize จะได้ warn กลับกัน ถ้าบนเครื่อง dev มี cache ค้างอยู่ Doctor จะขึ้น notice บอกว่าถ้าแก้โค้ดแล้วไม่เห็นผล ให้ลอง optimize:clear ซึ่งเป็นสาเหตุที่เจอบ่อยมาก
เขียน diagnostic ของโปรเจกต์เองได้
ของที่ Doctor ตรวจให้เป็นเรื่องทั่วไปของทุกแอป แต่โปรเจกต์ของเรามักมีเรื่องเฉพาะ เช่น Horizon ต้องรันอยู่ หรือต้องมี Chrome สำหรับสร้าง PDF ใช้ make:diagnostic สร้าง class ขึ้นมาได้
php artisan make:diagnostic HorizonIsRunning --fixable
ได้ class ใน app/Doctor/Diagnostics ที่มีเมธอด check() คืนผลเป็น $this->pass(), $this->warn() หรือ $this->fail() พร้อมข้อความที่บอกวิธีแก้ ถ้าใส่ --fixable ก็มีเมธอด fix() ให้เขียนวิธีแก้เพิ่ม แล้วลงทะเบียนใน service provider
use App\Doctor\Diagnostics\HorizonIsRunning;
use Laravel\Doctor\Facades\Doctor;
public function boot(): void
{
Doctor::diagnostic(HorizonIsRunning::class);
}
แพ็กเกจอื่นก็ลงทะเบียน diagnostic ของตัวเองแบบเดียวกันได้ รายงานจะบอกชื่อแพ็กเกจที่มาของแต่ละข้อไว้ข้าง ๆ
Doctor ไม่ได้มาแทน ค่าเริ่มต้นที่ควรตั้งตั้งแต่วันแรก หรือ test แต่ช่วยตอบคำถาม "เครื่องนี้พร้อมหรือยัง" ได้ในคำสั่งเดียว คู่กับ git hook ที่ sync เครื่องหลัง pull ก็ครอบคลุมเรื่องเครื่องเพื่อนเปิดไม่ขึ้นได้เกือบหมด
Doctor เปลี่ยนรูปแบบผลเองเมื่อ AI agent เป็นคนรัน
ถ้ารันจาก Claude Code, Cursor หรือ agent ตัวอื่น Doctor จะเห็นเองว่าเป็น agent แล้วตอบเป็น JSON บรรทัดเดียว ที่มีแค่ยอดรวมกับรายการที่ต้องแก้
{"tool":"doctor","result":"failed","diagnostics":27,"failed":1,"warnings":1,"notices":0,"passed":19,"skipped":6,"issues":[...]}
agent อ่านแล้วรู้ทันทีว่าต้องแก้อะไร ไม่ต้องอ่านกรอบสีที่มีไว้ให้คนดู ถ้าอยากเลือกเองก็ใส่ --format=cli, --format=json หรือ --format=github สำหรับ annotation ใน GitHub Actions
Laravel PAO ย่อผลของเครื่องมือ test ให้ agent
Laravel PAO ใช้แนวคิดเดียวกันกับเครื่องมือ test เวลา agent รัน test ทั้งชุด ผลแบบปกติมีจุดเป็นแถว ชื่อ test ทุกตัว และเวลาของแต่ละไฟล์ ทั้งหมดนั้นกิน context ของ agent ไปเปล่า ๆ ทั้งที่สิ่งที่ agent ต้องรู้คือผ่านไหม และถ้าไม่ผ่านพังตรงไหน
ติดตั้งแบบ dev แล้วไม่ต้องตั้งค่าอะไร
composer require laravel/pao --dev
PAO ทำงานกับ Pest, PHPUnit, Paratest, PHPStan และ Rector เฉพาะตอนที่ agent เป็นคนรัน โดยตรวจจาก environment ของ agent ด้วยแพ็กเกจ laravel/agent-detector เราพิมพ์ใน terminal เองก็ยังเห็นผลแบบเดิม ส่วน agent ได้แบบนี้
{"tool":"pest","result":"passed","tests":541,"passed":541,"assertions":4648,"duration_ms":28102}
test 541 ตัวของเว็บนี้เหลือบรรทัดเดียว ถ้ามีตัวที่ล้ม JSON จะมีรายการเฉพาะตัวนั้นพร้อมไฟล์ บรรทัด และข้อความ error ส่วน Pint กับ Doctor ตรวจหา agent เองโดยไม่ผ่าน PAO และตอบเป็น JSON แบบเดียวกัน
บางครั้งเราก็อยากเห็นผลแบบปกติ
PAO ดีกับ agent แต่บางครั้งเราให้ agent รันคำสั่งเพื่อเอาผลมาให้คนอ่าน เช่น copy ผลของ PHPStan มาใส่ในบทความ หรือดูรายละเอียดที่ JSON ตัดทิ้งไป ตอนนั้นให้ปิด PAO ด้วยตัวแปร environment
PAO_DISABLE=1 php artisan test --compact
PAO_DISABLE ใช้ได้กับเครื่องมือที่ผ่าน PAO เท่านั้น Pint กับ Doctor ไม่สนตัวแปรนี้ Doctor ใส่ --format=cli ได้ ส่วน Pint ให้รันโดยล้าง environment ของ agent ออกทั้งหมด
env -i HOME=$HOME PATH=$PATH TERM=xterm vendor/bin/pint --test
ทั้ง Doctor และ PAO ทำงานคู่กับ Laravel Boost ได้ดี Boost ให้ agent รู้จักโปรเจกต์ตอนเริ่มงาน Doctor บอกว่าเครื่องพร้อมหรือยัง ส่วน PAO ทำให้ผล test ที่ agent รันซ้ำหลายสิบรอบในหนึ่งงานไม่กิน context จนหมดครับ






