ลอง Claude Mods มาทั้งวัน กันของได้ดีกว่าที่คิด แล้วพอลองทำให้มันพัง มันก็เงียบไปเฉย ๆ
ลอง Claude Mods ทั้งวัน hook กัน Write ได้จริงและทำงานใน -p แต่ hook ที่พังจะถูกข้ามแบบเงียบ ๆ ไม่มีร่องรอยถ้าไม่เปิด debug จะเอาไปกันของอันตรายต้องเติม .catch() เอง
Claude Mods ลงแล้ว เอามาลองทั้งวัน ตั้งแต่ลงยันทำให้มันพัง
สรุปสั้น ๆ ก่อน ของมันดีกว่าที่คิด แต่มีเรื่องนึงที่ต้องรู้ก่อนเอาไปกันของจริง
ลงยังไงให้มันทำงาน
ต้อง Claude Code 2.1.269 ขึ้นไป ตอนนี้ออกถึง 2.1.273 แล้ว
เปิด flag ใน ~/.claude/settings.json
{
"env": {
"CLAUDE_CODE_ENABLE_FUNCTION_HOOKS": "1"
}
}
ไม่อยากแก้ไฟล์ ใช้ CLAUDE_CODE_ENABLE_FUNCTION_HOOKS=1 claude เอาก็ได้
ตรงนี้คือเรื่องที่ไม่มีใครบอก ใส่ env แล้วอาจยังไม่พอ มันมี rollout flag อีกชั้นที่ทำให้ module ไม่ถูกโหลด ถึงจะตั้ง env ถูกแล้วก็ตาม
เพราะงั้นลงเสร็จอย่าเพิ่งเชื่อว่าใช้ได้ รันด้วย --debug-file สักรอบ แล้วดูว่า module โหลดจริงไหม
หน้าตาของจริงเป็นยังไง
โครง plugin คือ .claude-plugin/plugin.json กับ hooks/hooks.json ที่ชี้ไปหา module
ในโมดูล export ฟังก์ชันชื่อ register ออกมา แล้วผูก event ที่อยากดัก
ดัก tool call ด้วย on('tool.call', ...) ตัว event ที่ได้มามี tool กับ argument ของมัน อย่าง Write ก็จะมี file_path กับ content มาให้เลย
จากตรงนั้นเลือกได้สามทาง คืน { deny: เหตุผล } เพื่อบล็อก เรียก next(e) เพื่อปล่อยผ่าน หรือคืน { result } เพื่อตอบแทนเองไปเลยโดยไม่ต้องให้ tool จริงทำงาน
โมดูลรันใน worker ไม่มี Node ไม่มี DOM แตะไฟล์ผ่าน $.fs
ของมันทำงานดีนะ
เขียน hook กัน Write ไม่ให้เขียนไฟล์นึง แล้วสั่งให้มันเขียน
โดนบล็อก โมเดลเห็น error แล้วหยุด
ลองสี่รอบ โดนบล็อกสี่รอบ แต่ละรอบมี tool call เดียว Write แล้วจบ ไม่มีรอบไหนที่หนีไปใช้ bash redirect หรือ python เขียนแทน ลองลดเหตุผลปฏิเสธให้เหลือแค่คำว่า nope ก็ยังไม่หนี
อันนี้เกินคาด เพราะที่คนพูดกันคือโมเดลจะอ้อมไปทางอื่น
แต่ติดดาวไว้ ลองแค่สี่รอบ โมเดลเดียว และ session ลูกมันอ่าน CLAUDE.md ไปด้วย ซึ่งในนั้นมีกฎห้ามข้าม guard อยู่แล้ว โมเดลตอบมาเองเลยว่า "ไม่ได้ลองหนีไปทาง shell เพราะมันจะข้าม guard ที่คุณตั้งไว้" เครื่องที่ไม่มีกฎแบบนี้ยังไม่รู้ ใครลองแล้วผลต่างมาเล่าหน่อย
ใครรัน automation ผ่าน -p อ่านตรงนี้
hook ที่ไม่ใช่ UI ยิงปกติใน claude -p ลองแล้ว session.start กับ tool.call เข้า log ครบ
แต่ ui.render ลงทะเบียนได้เฉย ๆ ไม่มีอะไรถูกวาดออกมา
แปลว่าดักได้ วาดไม่ได้ ใครมี cron หรือ bot ที่รัน headless อยู่ เอา mod ไปดัก tool call ได้จริง แค่อย่าหวังส่วนที่เป็นหน้าจอ
ทีนี้ลองทำให้มันพังบ้าง
guard ตัวเดิม ทำให้มันพังทีละแบบ แล้วสั่งเขียนไฟล์เดิมใหม่ทุกครั้ง
โยน error ไฟล์ถูกเขียน exit code 0 stderr ว่าง
ค้างไม่คืนค่า รอครบ 10 วินาทีตาม budget แล้วไฟล์ถูกเขียน รอบนั้น 19 วิ จากปกติ 9 วิ ช้าขึ้นเท่าตัวแล้วก็ยังทะลุ
คืนค่าผิดรูป ไฟล์ถูกเขียน
ว่างเปล่าไม่ทำอะไร ไฟล์ถูกเขียน
สี่แบบ ทะลุสี่แบบ
อันนี้ไม่ใช่บั๊ก ใน d.ts เขียนไว้ตรง ๆ ว่า hook ที่พังจะถูกข้าม แล้วให้ตัวล่างกับ core ทำงานแทน มันคือดีไซน์ ทุกแบบมี log บรรทัดนึงว่า hook failed: ... skipped; what is below it ran in its place
แล้วรอบที่ห้า อันนี้คือของจริง
ลองใหม่ทั้งหมด แต่ไม่เปิด --debug-file
ไฟล์ถูกเขียน ไม่มีร่องรอยที่ไหนเลย
stderr มีแต่ warning เรื่อง permission ที่ไม่เกี่ยว stdout คือโมเดลรายงานว่าเขียนไฟล์สำเร็จแล้วครับ ใน ~/.claude/debug/ มี symlink ชื่อ latest ชี้ไปที่ไฟล์ที่ไม่มีอยู่จริง
แล้วโมเดลก็เตือนไม่ได้ เพราะมันได้ผลลัพธ์ปกติกลับไป ในสายตามันคือเขียนสำเร็จ
ลองเช็ก stream-json ด้วย ใส่ --include-hook-events แล้ว event ที่ออกมาเป็น hook แบบเดิมทั้งหมด function hook ไม่ยิง event อะไรเลย ทั้งตอนทำงานทั้งตอนพัง
แปลว่าเขียนตัวเฝ้าจากข้างนอกไม่ได้ ไม่มี CI check ไม่มี alert เหลือทางเดียวคือเปิด debug แล้วไปไล่อ่านเอง
รอบแรกที่รัน ผลออกมาผิด แล้วเกือบเชื่อไปแล้ว
รอบแรกสุดที่รัน baseline ไฟล์ถูกเขียนทั้งที่ guard ควรกัน เกือบบันทึกลงผลไปแล้วว่า fail open
ที่จริงคือ path ของ plugin ชี้ผิด เพราะ cd เข้า directory ก่อนรัน plugin เลยไม่ถูกโหลดสักครั้ง
มันบอกไหม บอกว่า [WARN] Plugin path does not exist ... skipping ใน debug log ไม่ขึ้น stderr ไม่ขึ้นที่อื่น
เคสนี้แย่กว่าสี่แบบข้างบนอีก เพราะ guard ไม่ได้ตาย มันไม่เคยเกิดขึ้นเลยตั้งแต่แรก
คนที่ตั้งใจมานั่งทดสอบว่าของมันเงียบตอนพังไหม โดนความเงียบนั้นเล่นตั้งแต่รอบแรก
ทางแก้มีอยู่แล้ว แต่ต้องขอเอง
ใน d.ts มี .catch() ต่อท้าย hook ได้ เขียนไว้ว่ามันจะทำงานตอน hook โยน error เกิน budget หรือคืนค่าผิดรูป มี next.error บอกว่าเป็น throw หรือ timeout
ยังไม่ได้ลอง เลยไม่ยืนยันว่าใช้ได้จริง
แต่ประเด็นไม่ได้อยู่ตรงนั้น ประเด็นคือ fail-closed เป็นของที่ต้องขอ ใครไม่ขอก็ fail-open ไปโดยไม่รู้ตัว แล้วคนส่วนใหญ่ไม่รู้ว่ามีให้ขอ
สรุปสำหรับคนที่จะเอาไปใช้
เอาไปทำของเล่นบนหน้าจอ สนุกและปลอดภัย
เอาไปดัก tool call ใน automation ได้จริง ทำงานใน -p ด้วย
เอาไปกันของอันตราย ได้ แต่ต้องเติม .catch() แล้วอย่าเชื่อว่าเงียบแปลว่าปกติ
ในระบบนี้เงียบแปลว่าไม่รู้