รัน Claude Code ใน CI: -p, --output-format json, --json-schema
แนวคิด
ใน pipeline อัตโนมัติไม่มีคนคอยตอบ prompt ดังนั้นการรัน claude "..." เฉย ๆ จะค้างเพราะ Claude Code รอ input แบบ interactive คำตอบคือ flag -p หรือ --print ที่สั่งให้รันแบบ non-interactive คือประมวล prompt พ่นผลออก stdout แล้วออกโดยไม่รอ input นี่คือสิ่งที่ CI/CD ต้องการพอดี
พิจารณาโจทย์ตรง ๆ ว่า job รัน claude "วิเคราะห์ PR นี้หาช่องโหว่ความปลอดภัย" แล้วค้างไม่จบ log บอกว่ากำลังรอ interactive input ทางแก้ที่ถูกคือเติม -p เป็น claude -p "..." ส่วนตัวเลือกอื่นอย่าง environment variable CLAUDE_HEADLESS=true หรือ flag --batch เป็นฟีเจอร์ที่ไม่มีจริง และการ redirect stdin จาก /dev/null เป็น workaround ที่ไม่ตรงกับ syntax ของ Claude Code
ทำไมสำคัญ
เมื่อ CI ต้องเอาผลไป parse ต่อ เช่นโพสต์เป็น inline PR comment ผลที่เป็นข้อความอิสระใช้ยาก จึงมี flag คุมรูปแบบ output --output-format รับสามค่า คือ text (ค่าเริ่มต้น), json (มี result, session id, และ metadata), และ stream-json (JSON ต่อบรรทัดแบบ streaming) การใช้ --output-format json ทำให้ script ดึงฟิลด์ที่ต้องการได้แน่นอนด้วยเครื่องมืออย่าง jq
เมื่อต้องการให้ output ตรงตาม schema ที่กำหนด ใช้ --output-format json คู่กับ --json-schema แล้วใส่ JSON Schema เข้าไป Claude Code จะ validate ผลให้ตรง schema และวางผลที่มีโครงสร้างไว้ในฟิลด์ structured_output ของ response นี่คือกลไกที่ทำให้ CI ได้ machine-parseable structured finding ที่เชื่อถือได้ เอาไปโพสต์เป็น comment อัตโนมัติได้โดยไม่ต้องเดา format นอกจากนี้ยังใช้ --append-system-prompt เพื่อเสริมบทบาท เช่นสั่งให้ทำตัวเป็น security engineer โดยยังคงพฤติกรรมพื้นฐานของ Claude Code ไว้
ตัวอย่าง
# non-interactive กัน job ค้างเพราะรอ input
claude -p "วิเคราะห์ PR นี้หาช่องโหว่ความปลอดภัย"
# output เป็น JSON แล้วดึงฟิลด์ result ด้วย jq
claude -p "สรุปโปรเจกต์นี้" --output-format json | jq -r '.result'
# บังคับ structured output ให้ตรง schema → อยู่ในฟิลด์ structured_output
claude -p "ดึงชื่อฟังก์ชันหลักจาก auth.py" \
--output-format json \
--json-schema '{"type":"object","properties":{"functions":{"type":"array","items":{"type":"string"}}},"required":["functions"]}' \
| jq '.structured_output'
# เสริมบทบาทใน CI review
gh pr diff "$1" | claude -p \
--append-system-prompt "คุณคือ security engineer ตรวจหาช่องโหว่" \
--output-format json
เช็คความเข้าใจ
job รัน claude แล้วค้างเพราะรอ interactive input ควรแก้อย่างไร และทำไมตัวเลือกอื่นไม่ถูก
จะให้ CI ได้ output ที่ machine-parseable ตรงตาม schema ต้องใช้ flag ใด และผลอยู่ที่ไหน