ตั้งแต่เปิด Newton ขายจริง มีลูกค้าทักมาเรื่อยๆ ว่า "ขอใช้ Codex แทน Claude ได้ไหม" บางคนก็ "อยากลอง Antigravity ของ Google ดู ต่อใน Tim Chat ได้รึเปล่า" — ผมเลยให้ทิม (AI Agent ของผม) สร้าง Tim Chat v2 ที่รองรับ CLI หลายตัวพร้อมกัน เมื่อคืนนี้ (2026-05-28) Antigravity ก็เพิ่งเข้าระบบเป็น CLI ตัวที่ 3 — แต่ก่อนจะถึงตรงนั้น ดันเจอ 2 บั๊กแปลกๆ ที่เกือบทำให้ผมยกธงขาว

ทำไมต้องมี CLI หลายตัวให้เลือก

ตอน Newton เปิดวันแรก ทุก server ใช้ Claude อย่างเดียวครับ เพราะผมใช้ Claude มาตลอด รู้นิสัยมัน รู้ว่า prompt แบบไหนได้ผล skill 34 ตัวของทิมก็เขียนมาให้ Claude อ่าน

แต่ลูกค้าแต่ละคนมีของถนัดต่างกันครับ บางคนใช้ Codex (OpenAI) มาก่อน Claude บางคนพี่งสมัคร Google AI Pro แล้วอยากใช้ Antigravity เพราะมันแถมมากับ subscription บางคนอยากสลับไปสลับมา เปรียบเทียบเองว่าตัวไหนเขียน code เก่งกว่ากันใน context ของเขา

ตอนแรกผมคิดว่า "ก็ติดตั้งทั้ง 3 ตัวบน server ของลูกค้าก็จบสิ ลูกค้าสลับเอง" แต่พอลองทำจริงมันยุ่งเหยิงครับ — แต่ละ CLI มี config คนละแบบ login คนละทาง history เก็บคนละไฟล์ ลูกค้าต้องเปิด terminal เอง ต้องจำ command แต่ละตัว ขัดกับ value หลักของ Newton ที่ "เปิดมาใช้ได้เลย"

Tim Chat v2 — adapter pattern หลังบ้านตัวเดียว

ทิมเลย refactor Tim Chat ใหม่ทั้งระบบ แทนที่จะ hard-code call ไปหา Claude อย่างเดียว ก็แยกเป็น adapter pattern — มี adapters/claude.js, adapters/codex.js, adapters/antigravity.js ทุกตัว implement interface เดียวกัน ฝั่ง server.js เป็นแค่ WebSocket router บางๆ — รับข้อความจากหน้า chat แล้วเลือก adapter ตาม CLI ที่ลูกค้าเลือก

ฝั่ง UI ลูกค้าเห็นแค่ dropdown ตัวเดียว เลือก Claude / Codex / Antigravity แล้วพิมพ์ chat ตามปกติ ระบบจัดการที่เหลือเอง ไม่ต้องรู้ว่าหลังบ้านยิงไปหา CLI ตัวไหน (แต่การมีหลาย CLI ก็พ่วงปัญหาใหม่มาด้วย — event ดิบของแต่ละตัวเริ่มหลุดมาเป็น bubble รกหน้าจอลูกค้า จนผมต้องวาง protocol กรองทีหลัง)

Claude กับ Codex ขึ้นก่อน ทำงานได้ดี — แต่ Antigravity ดันยากกว่าที่คิด เพราะมันไม่มี mode --json เหมือน Codex และไม่มี API ตรงๆ ให้ยิง มันถูกออกแบบมาให้รันใน TUI สวยงามแบบ interactive อย่างเดียว เราต้องเปิดเป็น PTY แล้วคุยกันผ่าน input/output ของ terminal ตรงๆ

บั๊กที่ 1 — onboarding wizard กลืนคีย์

ทิม spawn process ของ Antigravity ขึ้นมา เปิด PTY ส่งข้อความแรกของลูกค้าเข้าไป — แล้วเงียบครับ ไม่มี output ไม่มี response ผ่านไป 30 วิยังไม่มีอะไรเด้งกลับมา

ไล่ดู terminal buffer ปรากฏว่า Antigravity เปิดมาเป็นหน้า "Welcome to Antigravity CLI" — wizard onboarding ถามว่า "ยอมรับ telemetry ไหม" + "trust workspace นี้ไหม" + กด Enter เพื่อเริ่ม โดย wizard ตัวนี้ "consume" ทุก keystroke ที่ส่งเข้ามา ข้อความของลูกค้าที่ส่งเข้าไปครั้งแรกเลยโดน wizard กลืนไปเฉยๆ ไม่ถึง chat session ด้วยซ้ำ

ปัญหาคือ ลูกค้าใหม่ทุกคนเปิด server ครั้งแรกจะเจอแบบนี้ — พิมพ์ข้อความ แล้วเงียบ ไม่มี error ไม่มี hint ว่าเกิดอะไรขึ้น UX แย่มาก

ทิมเลยเขียน _ensureSettings() ก่อน spawn ทุกครั้ง — pre-write ไฟล์ ~/.gemini/antigravity-cli/settings.json ใส่ enableTelemetry: false + trustedWorkspaces: [cwd] ไว้ก่อนเลย พอ Antigravity เปิดมาเห็นว่า settings มีแล้ว ก็ข้าม wizard ไปเลย เข้า prompt พร้อมรับ input ทันที — ลูกค้าพิมพ์ข้อความแรกก็ตอบกลับมาได้เลย

บั๊กที่ 2 — OAuth token เขียนผิด schema

พอแก้ wizard เสร็จ เปิดทดสอบกับ Google account ของผมเองก่อน — login ผ่าน ระบบบอกว่า "connected" แล้ว แต่พอเรียกใช้จริงดันบอกว่า "not authenticated" 555

ไล่ไปดู control plane (ระบบที่จัดการ OAuth ของ Newton) ปรากฏว่ามันเขียน token ผิด path + ผิด schema เลย — control plane เก่าเขียน oauth_creds.json แบบ flat:

{
  "access_token": "...",
  "refresh_token": "...",
  "expiry": "2026-05-29T..."
}

แต่ Antigravity อ่านไฟล์ชื่อ antigravity-oauth-token ที่เป็น nested envelope:

{
  "token": {
    "access_token": "...",
    "token_type": "Bearer",
    "refresh_token": "...",
    "expiry": "2026-05-29T..."
  },
  "auth_method": "consumer"
}

ผม "connected" สำเร็จก็จริง แต่ token ไปกองอยู่ผิดที่ + ผิด format Antigravity เลยอ่านไม่เจอ มันก็ขึ้น "not authenticated" ทุกครั้งที่ใช้

ทิมเขียน helper _agy_format_token() แปลง flat schema เป็น nested envelope แล้ว writes ไปไว้ที่ path ถูกต้อง พร้อมอัปเดต validator ให้ check path/format ใหม่ ทำให้ลูกค้าที่เคย "connected" ในระบบเก่าต้อง re-OAuth ใหม่หมด (ผมเขียน migration script ให้ทิมส่งเมลแจ้งทุกคนพร้อมลิงก์ login ใหม่)

shipped แล้ว — สิ่งที่ลูกค้าได้

เมื่อคืนนี้ทิม commit d84c657 + run smoke test ครบ 6 ขั้น (start → send → bubble → detach → reattach → list → delete) ผ่านทุกข้อ deploy ขึ้น staging buildnewton.incomeinclick.com — เปิด Tim Chat ขึ้นมา ตอนนี้มี dropdown ให้เลือก 3 ตัวแล้วครับ

  • Claude — ตัว default ที่ผมใช้เอง รู้นิสัย รู้ skill 34 ตัวของทิม
  • Codex — สำหรับลูกค้าที่ถนัด OpenAI หรืออยากใช้ GPT engine
  • Antigravity — Google CLI สำหรับลูกค้าที่มี Google AI Pro อยู่แล้ว ใช้ quota ฟรีที่ติดมากับ subscription ได้เลย

ลูกค้าสลับได้กลางทาง ไม่ต้อง logout ไม่ต้อง restart ระบบจำว่าแต่ละ session ใช้ CLI ตัวไหน เก็บ history แยกของใครของมัน ของแถมคือทิม implement reattach across disconnect — ถ้าเน็ตลูกค้าหลุดกลาง chat กลับมาเชื่อมต่อใหม่ก็เห็น history เดิมต่อจากเดิมได้เลย ไม่ขึ้นใหม่ (เหมือนเวลาผม push fix ข้าม server มันก็ deploy ต่อจากที่ค้างไว้ได้)

บทเรียน — Adapter pattern คือเพื่อนแท้ของ multi-CLI

สิ่งที่ผมเรียนรู้จากงานคืนนี้:

1. แยก layer ให้ชัดตั้งแต่แรก — ถ้าผมไม่ได้ refactor เป็น adapter pattern ก่อน การเพิ่ม CLI ตัวที่ 3 คงต้องไปแก้ทั่ว codebase ทุกจุดที่ call Claude พอแยกเป็น adapter แล้ว เพิ่ม CLI ใหม่ = สร้างไฟล์ใหม่ 1 ไฟล์ + ลงทะเบียนใน registry จบ ไม่ต้องแตะ core เลย

2. CLI ที่ "interactive อย่างเดียว" ยากกว่า CLI ที่มี --json — Codex มี --json เลยง่าย Antigravity ไม่มี ต้อง PTY + ต้อง handle wizard + ต้อง screen-scrape output มันยุ่งกว่าหลายเท่า การเลือก CLI ใหม่มาต่อ ต้องเช็คก่อนว่ามี machine-readable mode ไหม ถ้าไม่มี เตรียมเหนื่อย

3. ปัญหาส่วนใหญ่ของ tool ใหม่อยู่ที่ "first-run experience" — onboarding wizard, OAuth flow, settings file ที่ยังไม่มี ทุกอย่างที่ต้องตั้งก่อนใช้งานจริง คือจุดที่บั๊กมักโผล่มา ทิมเลยใส่ pre-flight check ทุก CLI ใหม่ที่เพิ่มเข้าระบบ — เช็คก่อนว่า config ครบ token พร้อม wizard ข้ามได้ ก่อนปล่อยให้ user ส่ง message แรก

4. AI Agent ที่ทำงานต่อเนื่องได้ ทำให้ refactor ใหญ่กล้ามากขึ้น — Newton v2 ไม่ใช่งาน 1 session มันเป็นงานเป็นเดือนๆ ทิมทำงานต่อกันได้เพราะมีระบบ memory ที่จำได้ทุกอย่าง สถานะ Phase 1/2/3 ของแต่ละ adapter, OAuth schema ที่ลองแล้ว, ปัญหาที่ปิดไปแล้ว — ทุกอย่างอยู่ใน memory เปิด session ใหม่ทิมก็รู้ทันทีว่าทำต่อตรงไหน (แต่พอรองรับ 3 engine แล้ว ผมถึงเจอว่ามีตัวเดียวที่ความจำหายทุกครั้งที่เปิดแชทใหม่ — เลยต้องป้อน memory ให้มันตั้งแต่ประโยคแรก)

ของพวกนี้ถ้าผมทำเอง ใช้เวลาหลายสัปดาห์

ผมไม่ได้เขียน Antigravity adapter ด้วยตัวเองสักบรรทัดครับ ผมแค่บอกทิมว่า "อยากให้รองรับ Antigravity ด้วย" แล้วทิมก็ไป research เอง อ่าน Antigravity CLI source code เอง ทดลอง spawn process เอง เจอ wizard เอง เจอ OAuth schema ผิดเอง แก้เอง — ผมแค่อยู่หลังจอ approve กับให้ feedback เป็นระยะ

ถ้าผมเขียนเอง น่าจะใช้เวลา 1-2 สัปดาห์เป็นอย่างต่ำ เพราะต้องไป research mechanism ของ Antigravity, ต้องลองผิดลองถูก OAuth, ต้องไปทำ ANSI parser ฯลฯ — แต่ทิมจบใน 1 คืน

แถมการมี Antigravity เข้ามาเป็นตัวที่ 3 ยังพาเซอร์ไพรส์ดีๆ มาให้ผมทีหลังด้วย — มันมีเครื่องมือสร้างรูปในตัวที่นับในโควต้า subscription ที่ผมจ่ายอยู่แล้ว ทำให้ผมย้าย pipeline สร้างรูปบล็อกมาใช้มันจนค่ารูปต่อใบเหลือ 0 บาท

คำถามที่พบบ่อย

Claude, Codex และ Antigravity ต่างกันยังไง ควรใช้ตัวไหน

Claude เก่งเรื่อง reasoning และเขียนโค้ดซับซ้อน เหมาะสำหรับงาน multi-step ที่ต้องคิดยาว Codex ของ OpenAI ถนัดงาน coding และ tool use Antigravity ของ Google มาพร้อม Google AI Pro subscription ที่หลายคนมีอยู่แล้ว และมีเครื่องมือสร้างรูปในตัวโดยไม่เสียเงินเพิ่ม ลองใช้แล้วเปรียบเทียบเองได้เลยครับ เพราะแต่ละคนมี workflow ต่างกัน

สลับ CLI กลางทางได้เลยไหม หรือต้อง logout แล้วเริ่มใหม่

สลับได้ทันทีครับ แค่เลือก dropdown บนหน้าแชทแล้วเปลี่ยน ระบบจำว่าแต่ละ session ใช้ CLI ตัวไหน เก็บ history แยกของใครของมัน ถ้าเน็ตหลุดกลางคัน กลับมาเชื่อมต่อใหม่ก็เห็น history เดิมต่อจากเดิมได้เลย ไม่ขึ้นใหม่

ทำไม CLI ที่ออกแบบมาสำหรับ interactive TUI ถึง integrate ยากกว่าตัวอื่น

CLI แบบ interactive อย่าง Antigravity ถูกออกแบบมาสำหรับคนนั่งพิมพ์หน้าจอ ไม่ใช่สำหรับให้โปรแกรมอื่นคุยด้วย ต้องเปิดเป็น PTY แล้วส่ง input/output ผ่าน terminal ตรงๆ ซึ่งซับซ้อนกว่า CLI ที่มี --json mode มาก เพราะต้องจัดการ onboarding wizard, parse ANSI escape codes และ handle OAuth flow ที่ต่างออกไป

Adapter pattern ที่ใช้คืออะไร ทำไมถึงดีกว่าเขียนโค้ดรองรับทุก CLI รวมกัน

Adapter pattern คือการแยกโค้ดที่คุยกับแต่ละ CLI ออกเป็นไฟล์ของตัวเอง แล้วทำให้ทุกตัว implement interface เดียวกัน ข้อดีคือเพิ่ม CLI ใหม่เท่ากับสร้างไฟล์ใหม่ 1 ไฟล์โดยไม่ต้องแตะโค้ดส่วนกลางเลย ถ้าไม่ทำแบบนี้ การเพิ่ม CLI ตัวที่ 3 จะต้องไปแก้ทั่ว codebase ทุกจุดที่เคย hard-code call ไปหาตัวเดิม

ถ้าคุณก็อยากมี AI Agent ของตัวเองแบบนี้ — server ส่วนตัว + CLI หลายตัวให้เลือก (Claude / Codex / Antigravity) + apps พร้อมใช้ + memory + skill ที่สอนได้ตามใจ — ตอนนี้Newton เปิดให้ลูกค้าใหม่ลงทะเบียนได้แล้วครับ ลูกค้าทุกคนได้ Tim Chat v2 ที่มี multi-CLI พร้อมใช้ตั้งแต่นาทีแรก เซ็ตให้เสร็จภายใน 10 นาที — และทั้ง 3 CLI ผมจัดให้นั่งบน subscription เหมารายเดือน ไม่ใช่ฝั่ง API จ่ายตามโทเคนที่บิลบานปลายได้