Claude Code Hooks: 5 Pre-commit Recipes ตัวอย่างทีม Dev ไทย

ทีม dev ที่ผมคุยด้วยล่าสุดเล่าให้ฟังว่า Claude Code เผลอรัน rm -rf ใน config folder ตอนตี 2 — โชคดีที่ commit ล่าสุดคือ 5 นาทีก่อน เลยเสียแค่ commit เดียว แต่ถ้าเป็น production server จริงๆ คงไม่ตลกแบบนี้แน่ ปัญหาคือ เราจะเชื่อใจ AI agent ที่มี shell access แค่ไหน ก่อนที่มันจะทำพลาด

คำตอบที่ผมใช้กับทีมตัวเองและกับลูกค้าทุกที่คือ Hooks — ระบบ event-driven ที่ Claude Code มีให้ตั้งแต่ปี 2025 แต่คนไทยส่วนใหญ่ยังไม่ได้ใช้กันจริงจัง บทความนี้ผมจะให้ 5 recipes พร้อม copy-paste ที่ทีม dev ไทยควรมีใน .claude/settings.json ตั้งแต่วันแรกที่เริ่มใช้ Claude Code

TL;DR — สรุปสำหรับคนรีบ

Claude Code Hooks คือ shell script ที่รันอัตโนมัติในจุดต่างๆ ของ lifecycle (ก่อนใช้ tool, หลังใช้ tool, ตอนหยุดตอบ) — ไม่ใช่แค่ prompt แต่เป็น deterministic guardrails ที่บังคับใช้นโยบายได้จริง 5 recipes ที่ผมแนะนำ:

  1. Block rm -rf — PreToolUse matcher Bash, exit 2 ถ้าเจอคำสั่งอันตราย
  2. Prettier auto-format — PostToolUse matcher Edit|Write, รัน formatter อัตโนมัติทุกครั้งที่แก้ไฟล์
  3. LINE notify on Stop — Stop hook ส่ง notification เข้า LINE Notify เมื่อ Claude ทำงานเสร็จ
  4. Secret scanning — PreToolUse matcher Edit|Write, สแกนหา API key/password ก่อนเขียนลงไฟล์
  5. Test gate — Stop hook ที่บังคับให้ test pass ก่อน Claude จะหยุดตอบได้

ทุก recipe ทำงานแบบ event-driven ไม่ต้องพึ่ง prompt — Claude ลืม/มองข้ามไม่ได้ครับ

ทำไม Hooks สำคัญสำหรับทีม Dev ไทย

ผมเจอคำถามจากทีม dev แทบทุกที่ว่า “AI agent มันเชื่อถือได้แค่ไหน?” — คำตอบคือ มันเชื่อถือได้เท่าที่เราตั้ง guardrails ให้ ไม่ใช่เชื่อใจ vibe ของ prompt

Prompt-based instructions มีปัญหา 4 ข้อ ตามที่ heyuan110 สรุปไว้: (1) Claude อาจจะลืม (2) Claude อาจจะตัดสินใจว่าไม่จำเป็น (3) Claude block ตัวเองไม่ได้ก่อนที่ operation จะเกิด (4) บังคับ security policy ผ่านภาษาธรรมชาติไม่ได้

Hooks แก้ทั้ง 4 ข้อ เพราะมันเป็น shell script ที่รันแน่นอนทุกครั้ง — ไม่ขึ้นกับว่า Claude จะจำได้หรือไม่ และที่สำคัญคือ PreToolUse hook ทำงาน ก่อน permission mode check หมายความว่าแม้ user จะใช้ --dangerously-skip-permissions hook ก็ยังบังคับ deny ได้อยู่ ตาม official docs

💡 ในความเห็นของผม ทีมที่ใช้ Claude Code โดยไม่มี hook อย่างน้อย 3-4 ตัว = ทีมที่ฝาก credit card ให้ลูกถือไว้ แล้วบอกว่า “อย่าเผลอใช้นะลูก” — มันไม่ใช่เรื่อง trust แต่เป็นเรื่อง system design

Recipe #1: Block rm -rf และ DROP TABLE ก่อนรัน

Event: PreToolUse | Matcher: Bash | Goal: หยุดคำสั่งทำลายล้างก่อนถึง shell

นี่คือ hook ตัวแรกที่ทุกทีมควรมี เพราะ rm -rf, DROP TABLE, หรือ git push --force โดน undo ไม่ได้ ใส่ลง .claude/settings.json:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/block-dangerous.sh"
          }
        ]
      }
    ]
  }
}

แล้วสร้าง .claude/hooks/block-dangerous.sh:

#!/bin/bash
COMMAND=$(jq -r '.tool_input.command')

# Block patterns ที่ทำลายไม่ได้กลับ
BLOCK_PATTERNS='rm -rf|rm -fr|DROP TABLE|TRUNCATE|git push --force|git push -f|git reset --hard origin'

if echo "$COMMAND" | grep -qE "$BLOCK_PATTERNS"; then
  jq -n --arg cmd "$COMMAND" '{
    hookSpecificOutput: {
      hookEventName: "PreToolUse",
      permissionDecision: "deny",
      permissionDecisionReason: "Destructive command blocked: \($cmd). Ask human for review."
    }
  }'
  exit 0
fi
exit 0

อย่าลืม chmod +x .claude/hooks/block-dangerous.sh ครับ ถ้าเจอ rm -rf hook จะ return JSON บอก Claude ว่า deny พร้อมเหตุผล — Claude จะเอาเหตุผลนั้นไปคิดต่อและถามคนแทน ไม่ใช่หยุดเฉยๆ

Recipe #2: Prettier Auto-Format ทุกครั้งที่แก้ไฟล์

Event: PostToolUse | Matcher: Edit|Write | Goal: ไม่ต้องไปบ่นทีหลัง

เรื่องนี้คนเจอเยอะ — Claude เขียน logic ดี แต่ format มั่ว ไม่เคารพ tab/space ของ project ใส่ hook ตามนี้:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write|MultiEdit",
        "hooks": [
          {
            "type": "command",
            "command": "FILE=$(jq -r '.tool_input.file_path // empty') && [ -n \"$FILE\" ] && npx prettier --write \"$FILE\" 2>/dev/null || true"
          }
        ]
      }
    ]
  }
}

เคล็ดลับคือ || true ตอนท้าย — ถ้า Prettier fail (เช่น ไฟล์ binary หรือไม่ใช่ภาษาที่รองรับ) hook จะไม่บล็อก Claude ครับ official guide ก็ใช้ pattern คล้ายๆ กัน

ทีมที่ใช้ Python ก็เปลี่ยน npx prettier --write เป็น black หรือ ruff format ได้เลย Go ก็ gofmt -w ครับ ถ้าทีมใช้หลายภาษาในโปรเจกต์เดียว เขียน script ตรวจ extension แล้ว dispatch ก็ได้

Recipe #3: LINE Notify เมื่อ Claude ทำงานเสร็จ

Event: Stop | Goal: ไม่ต้องนั่งเฝ้า terminal

Recipe นี้เหมาะกับคนไทยที่ใช้ LINE เป็น primary messenger — ตั้ง LINE Messaging API token แล้วยิง notification เมื่อ Claude finish turn ครับ (หมายเหตุ: LINE Notify ตัวเก่าหยุดให้บริการ 31 มี.ค. 2025 ใช้ LINE Messaging API + push message แทน)

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/notify-line.sh"
          }
        ]
      }
    ]
  }
}

Script .claude/hooks/notify-line.sh:

#!/bin/bash
# ใช้ LINE Messaging API push (token จาก https://developers.line.biz)
TOKEN="${LINE_CHANNEL_TOKEN}"
USER_ID="${LINE_USER_ID}"
PROJECT=$(basename "$CLAUDE_PROJECT_DIR")

curl -s -X POST https://api.line.me/v2/bot/message/push \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d "{
    \"to\": \"$USER_ID\",
    \"messages\": [{
      \"type\": \"text\",
      \"text\": \"Claude เสร็จงานแล้วใน project: $PROJECT\"
    }]
  }" > /dev/null
exit 0

เก็บ token ใน environment variable อย่าฮาร์ดโค้ด — ผมเคยเห็นทีมไทยเอา token ใส่ใน script แล้ว push ขึ้น GitHub สาธารณะ โชคดีที่ LINE rotate token เร็ว ไม่งั้นโดนสแปม bot แน่

Recipe #4: Secret Scanning ก่อนเขียนลงไฟล์

Event: PreToolUse | Matcher: Edit|Write | Goal: กัน API key หลุด

นี่คือ hook ที่ผมเชื่อว่า ทุก enterprise project ต้องมี เพราะ Claude อาจจะเผลอ paste API key จาก context กลับลงในไฟล์ตอน refactor:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|Write|MultiEdit",
        "hooks": [
          {
            "type": "command",
            "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/secret-scan.sh"
          }
        ]
      }
    ]
  }
}

Script .claude/hooks/secret-scan.sh:

#!/bin/bash
INPUT=$(cat)
CONTENT=$(echo "$INPUT" | jq -r '.tool_input.content // .tool_input.new_string // empty')

# Patterns ที่บ่งชี้ว่าเป็น real secret
PATTERNS=(
  'sk-ant-[a-zA-Z0-9_-]{40,}'        # Anthropic API key
  'sk-proj-[a-zA-Z0-9_-]{40,}'       # OpenAI project key
  'AKIA[0-9A-Z]{16}'                  # AWS access key
  'AIza[0-9A-Za-z_-]{35}'             # Google API key
  'ghp_[a-zA-Z0-9]{36}'               # GitHub PAT
  'xox[baprs]-[a-zA-Z0-9-]+'          # Slack token
)

for PATTERN in "${PATTERNS[@]}"; do
  if echo "$CONTENT" | grep -qE "$PATTERN"; then
    # ตรวจว่าไม่ใช่ placeholder
    if ! echo "$CONTENT" | grep -qE "(YOUR_|REPLACE_|EXAMPLE|PLACEHOLDER|process\.env|os\.getenv)"; then
      echo "BLOCKED: Real secret detected. Use environment variable instead." >&2
      exit 2
    fi
  fi
done
exit 0

Pattern หลักๆ ตามนี้ ที่สำคัญคือ placeholder check — ถ้า content มีคำว่า process.env หรือ YOUR_KEY_HERE แสดงว่าเป็น template code ไม่ใช่ secret จริง ไม่ต้อง block whoffagents เขียน pattern คล้ายๆ กันใน TypeScript ปรับใช้ตาม stack ของทีมได้ครับ

Recipe #5: Test Gate — Claude หยุดไม่ได้ถ้า test ไม่ผ่าน

Event: Stop | Goal: บังคับให้ AI ทำงานจบจริง

Recipe นี้สำหรับทีมที่อยากให้ Claude “ปิดงานจริง” ไม่ใช่บอกว่าเสร็จแล้วแต่ test แดง ตั้ง Stop hook ที่ exit 2 ถ้า test fail — Claude จะ ถูกบังคับให้ทำต่อ จนกว่า test จะ pass:

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/test-gate.sh"
          }
        ]
      }
    ]
  }
}

Script .claude/hooks/test-gate.sh:

#!/bin/bash
# Read stop_hook_active เพื่อกัน infinite loop
STOP_ACTIVE=$(jq -r '.stop_hook_active // false')
if [ "$STOP_ACTIVE" = "true" ]; then
  exit 0  # ปล่อยผ่านรอบที่สอง
fi

# รัน test (ปรับตาม stack)
cd "$CLAUDE_PROJECT_DIR" || exit 0
TEST_OUTPUT=$(npm test 2>&1)
TEST_EXIT=$?

if [ $TEST_EXIT -ne 0 ]; then
  echo "Tests failing. Fix before stopping:" >&2
  echo "$TEST_OUTPUT" | tail -30 >&2
  exit 2  # บังคับ Claude ให้ทำต่อ
fi
exit 0

⚠️ ระวัง infinite loop — เช็ค stop_hook_active เสมอ ไม่งั้น Claude จะวนซ่อม test ไม่จบ Anthropic เตือนเรื่องนี้ใน official guide ครับ

Hooks ทำงานยังไง — สรุปสั้นๆ

ก่อนเอา recipe ข้างบนไปใช้ ขอ refresh concept สั้นๆ ครับ Claude Code 2.x มี 21 lifecycle events และ 4 handler types (command, prompt, agent, ตัวที่ 4 คือ MCP elicitation) ผมเขียนรายละเอียดทั้งหมดไว้ใน บทความ Claude Code Hooks 25 Events และ 4 Handler Types แต่สำหรับ recipe ข้างบนนี้รู้แค่ 4 events พอ:

  • PreToolUse — รันก่อน tool execute, exit 2 = block
  • PostToolUse — รันหลัง tool สำเร็จ, exit 2 = แค่ feedback (ไม่ undo)
  • Stop — รันเมื่อ Claude finish turn, exit 2 = บังคับทำต่อ
  • UserPromptSubmit — รันก่อน Claude เริ่มประมวลผล prompt, exit 2 = ปฏิเสธ prompt

Hook รับ context ผ่าน stdin เป็น JSON และ return ผลลัพธ์ผ่าน exit code + stdout/stderr ครับ ถ้าอยาก control ละเอียดขึ้น ใช้ hookSpecificOutput.permissionDecision JSON ตาม Recipe #1

ข้อควรระวัง — Pitfalls ที่เจอบ่อย

ก่อนปล่อย hook เข้า production มี 4 จุดที่ต้องระวัง

1. Permission ของ shell script — ทุก script ใน .claude/hooks/ ต้อง chmod +x ไม่งั้น Claude Code จะ silent fail (เห็น hook error ใน transcript เฉยๆ) ผมเคยใช้เวลา 30 นาที debug เพราะลืมเรื่องนี้

2. jq ไม่ได้ติดมากับทุกระบบ — Linux server ส่วนใหญ่ต้อง apt install jq ก่อน macOS ก็ brew install jq ทุก script ที่ใช้ jq ใส่ check ตอนต้นดีกว่า

3. timeout default 60 วินาที — ถ้า test suite รัน 5 นาที hook จะถูก kill ใส่ "timeout": 600000 (มิลลิวินาที) ใน hook config ครับ

4. PostToolUse exit 2 ไม่ undo — ผมเห็นทีมพยายามใช้ PostToolUse บล็อก operation บ่อยมาก ซึ่งไม่ได้ผล เพราะ tool รันไปแล้ว ใช้ PreToolUse ถ้าจะบล็อกจริงๆ eesel AI สรุปประเด็นนี้ไว้ดีครับ

FAQ คำถามที่เจอบ่อย

Hooks ต่างจาก permission rules ใน settings.json ยังไง?

Permission rules เป็น static allow/deny list — เหมาะกับกฎง่ายๆ เช่น “ไม่อนุญาต Bash” Hooks เป็น programmable — เขียน logic ตรวจสอบเงื่อนไขซับซ้อนได้ เช่น “block rm -rf เฉพาะใน production folder” ที่ permission rules ทำไม่ได้ ที่สำคัญคือ PreToolUse hook ทำงานก่อน permission check หมายความว่ามัน override ได้แม้ user จะใช้ bypass mode

ใช้ Hooks ใน Claude Code Plugin หรือ Skill ได้ไหม?

ได้ครับ — ตั้งแต่ Claude Code 2.x มี skill/plugin frontmatter รองรับ hooks ตรงๆ เขียน hooks: ใน frontmatter ของ skill ได้เลย hook จะ active เฉพาะตอนที่ skill นั้นถูก invoke ครับ ผมใช้ pattern นี้กับ skill line-flex-sender ของ Data-Espresso เพื่อ validate Flex Message ก่อนส่งทุกครั้ง — กัน quota error ได้ดี

ถ้าทีมใช้ Cursor หรือ Windsurf จะมี Hooks เหมือนกันไหม?

Cursor และ Windsurf ยังไม่มี hook system ระดับเดียวกับ Claude Code ครับ Cursor มี .cursorrules ที่เป็น prompt-based instructions Windsurf มี Cascade rules ก็ prompt-based เช่นกัน — ไม่ใช่ deterministic guardrails สำหรับทีมที่ต้องการ enforce policy ระดับ enterprise (เช่น compliance, secret scanning) Claude Code Hooks ยังเป็น tier เดียวที่ทำได้จริง อ่านเปรียบเทียบเชิงลึกได้ใน Claude Code vs Cursor 2026 สำหรับทีม dev ไทย

Hooks ส่งผลต่อ performance ของ Claude Code ไหม?

ส่งผลครับ — แต่ปกติไม่เกิน 100-500ms ต่อ tool call ถ้า hook ของคุณรัน script เบาๆ (เช็ค pattern, scan content) ปัญหาเริ่มเกิดเมื่อ hook รัน npm test หรือ TypeScript compile ที่กินเวลา 30+ วินาที — ทีมผมแก้ด้วยการให้ test gate hook รันแค่ test ที่เกี่ยวข้องกับไฟล์ที่เพิ่งแก้ ไม่ใช่ full suite

สรุปท้ายบท

ทีม dev ที่ใช้ Claude Code โดยไม่มี hook อย่างน้อย 3 ตัว = ทีมที่หวังพึ่ง vibe ของ AI 💡 ในความเห็นของผม 5 recipes ในบทความนี้คือ minimum viable safety net ที่ ทุกทีมต้องมีตั้งแต่วันแรก:

  • Recipe #1 (block destructive) → ป้องกันอุบัติเหตุระดับลบ production
  • Recipe #2 (auto-format) → กำจัดงานน่าเบื่อ
  • Recipe #3 (LINE notify) → ให้ AI ทำงานคู่กับ workflow คนไทยจริง
  • Recipe #4 (secret scan) → กัน compliance disaster
  • Recipe #5 (test gate) → บังคับ AI ปิดงานจริง

ถ้าอยากเริ่มต้น Claude Code ให้ครบลูป อ่าน คู่มือฉบับสมบูรณ์ 2026 ก่อน แล้วค่อยมาตั้ง hook ตามนี้ครับ การตั้ง guardrails ที่ดี ก็เหมือนการใส่ seat belt — ไม่ได้บอกว่าคุณขับรถไม่เก่ง แต่บอกว่าคุณเข้าใจว่าอุบัติเหตุเกิดได้เสมอ

ข้อมูลอัปเดต: เมษายน 2026 (Claude Code 2.x)

Leave a Comment

สอบถามข้อมูล
Scroll to Top