Bài 9 · Nâng cao · 22 phút· Cập nhật 11/06/2026
Hooks: tự động hoá vòng đời
Biên soạn bởi Nguyễn Anh Tuấn
Chạy lệnh tại các mốc (PreToolUse/PostToolUse/Stop…): tự format/test, chặn lệnh nguy hiểm; cấu hình trong settings.json, mã thoát.
CLAUDE.md rất hữu ích, nhưng nó là lời khuyên - Claude cố tuân theo, đôi khi vẫn quên. Khi bạn cần một điều luôn luôn xảy ra (format sau mỗi lần sửa, chặn lệnh xoá nguy hiểm), bạn cần đảm bảo, không phải lời khuyên. Đó là hooks: mã do bạn cấu hình, Claude Code tự chạy tại các mốc vòng đời cố định.
- ▸CLAUDE.md = LỜI KHUYÊN (có thể quên); Hook = ĐẢM BẢO (luôn chạy, do máy thực thi).
- ▸Hook chạy tại các mốc: trước/sau khi dùng công cụ, đầu phiên, khi Claude trả lời xong…
- ▸Dùng để: tự format/test, chặn lệnh nguy hiểm, ghi log, nhắc nhở.
Hook gắn vào một “mốc”. Vài mốc hay dùng nhất:
| Mốc | Khi nào | Chặn được? |
|---|---|---|
| SessionStart | Mở/khôi phục phiên | không |
| UserPromptSubmit | Bạn gửi một prompt | có |
| PreToolUse | TRƯỚC khi chạy một công cụ | có |
| PostToolUse | SAU khi công cụ chạy xong | đưa ý cho Claude |
| Stop | Claude trả lời xong | có (bắt làm tiếp) |
| PreCompact | Trước khi nén ngữ cảnh | có |
| SessionEnd | Kết thúc phiên | không |
- ▸PreToolUse là mốc mạnh nhất: chạy TRƯỚC công cụ → CHẶN được hành động.
- ▸PostToolUse chạy SAU → hợp để tự format/test (công cụ đã chạy, không chặn nữa).
- ▸Stop chặn được để buộc Claude làm tiếp (vd “chưa chạy test thì chưa dừng”).
Thử ngay - bật/tắt hook để xem vòng đời thay đổi ra sao:
Ba lệnh gọi cố định: Edit(src/app.ts), Bash(rm -rf build), Bash(npm test). Bật/tắt hook để xem thứ tự vòng đời thay đổi ra sao:
Timeline vòng đời
- ⏺ công cụ Edit(src/app.ts)
- PostToolUse PostToolUse: tự format (Edit|Write) src/app.ts
- PreToolUse PreToolUse: chặn rm (Bash) "rm " khớp trong "rm -rf build"
- ⏺ công cụ Bash(rm -rf build) bị chặn bởi hook
- PreToolUse PreToolUse: chặn rm (Bash) npm test
- ⏺ công cụ Bash(npm test)
Bị chặn (1): rm -rf build
Hook khai báo trong settings.json theo cấu trúc: Mốc → matcher (lọc công cụ) → danh sách hook. Ví dụ kinh điển: tự chạy linter sau mỗi lần Claude ghi/sửa file:
.claude/settings.json - tự lint sau khi sửa file
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.file_path' | xargs npm run lint:fix"
}
]
}
]
}
} - ▸matcher lọc công cụ: "Bash", "Edit|Write" (nhiều cái), "*" (mọi công cụ).
- ▸Mỗi hook có type - phổ biến nhất là "command" (chạy một script shell).
- ▸Nơi đặt: .claude/settings.json (dự án) · ~/.claude/settings.json (cá nhân) · .claude/settings.local.json (cục bộ).
Mốc PreToolUse chạy TRƯỚC công cụ, nên nó chặn được. Hook nhận dữ liệu sự kiện qua stdin (JSON) và quyết định bằng mã thoát hoặc JSON trả về:
.claude/hooks/block-rm.sh - chặn rm -rf
#!/bin/bash
COMMAND=$(jq -r '.tool_input.command')
if echo "$COMMAND" | grep -q 'rm -rf'; then
jq -n '{
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "deny",
permissionDecisionReason: "Chan lenh rm -rf theo chinh sach"
}
}'
else
exit 0
fi - ▸Hook nhận JSON qua stdin: session_id, tool_name, tool_input… (dùng jq để đọc).
- ▸Mã thoát 0 = ổn; 2 = CHẶN (stderr đưa cho Claude); mã khác = không chặn, ghi log.
- ▸Cách hiện đại: in JSON với permissionDecision "deny" + lý do - rõ ràng cho người dùng.
- ▸Trỏ tới script bằng ${CLAUDE_PROJECT_DIR}/.claude/hooks/block-rm.sh để chắc đường dẫn.
Ngoài command (chạy script), hook còn có thể là: http (gọi một dịch vụ), mcp_tool (gọi công cụ MCP), prompt (hỏi một model đánh giá), hay agent (một subagent kiểm tra). Phần lớn việc thường ngày chỉ cần command.
Sức mạnh đi kèm rủi ro
Tiếp theo: MCP
Câu hỏi thường gặp
CLAUDE.md là LỜI KHUYÊN: Claude đọc và CỐ tuân theo, nhưng vẫn có thể quên/bỏ qua. Hook là ĐẢM BẢO: một đoạn mã do bạn cấu hình, Claude Code TỰ chạy tại một mốc cố định - không phụ thuộc vào việc Claude “nhớ” hay không. Cần “mỗi lần sửa file phải format” thì hook chắc chắn hơn nhắc trong CLAUDE.md.
Nhiều, nhưng hay dùng nhất: SessionStart (mở phiên), UserPromptSubmit (bạn gửi prompt), PreToolUse (TRƯỚC khi chạy công cụ - chặn được), PostToolUse (SAU khi công cụ chạy xong), Stop (Claude trả lời xong - chặn để bắt làm tiếp), SubagentStop, PreCompact (trước khi nén ngữ cảnh), SessionEnd. Một số mốc CHẶN được hành động, một số chỉ để quan sát.
Trong settings.json (mục hooks): cấp dự án .claude/settings.json (chia sẻ qua git), cấp cá nhân ~/.claude/settings.json, hoặc cục bộ .claude/settings.local.json. Plugin cũng đóng gói hook được (hooks/hooks.json). Gõ /hooks để xem các hook đang bật.
0 = thành công (nếu in JSON ra stdout sẽ được xử lý). 2 = lỗi CHẶN: stderr được đưa cho Claude/người dùng và hành động bị chặn (vd PreToolUse → không cho chạy công cụ; Stop → bắt Claude làm tiếp). Mã khác = lỗi KHÔNG chặn: ghi vào log, vẫn chạy tiếp. Hook nhận dữ liệu sự kiện dưới dạng JSON qua stdin.
Tick những điều em tự tin làm được. Càng lên cao, em càng hiểu sâu.
Trả lời vài câu để chắc rằng em đã nắm bài.
Khác biệt cốt lõi giữa CLAUDE.md và hook là gì?
- 1
Khuyên vs đảm bảo
Bằng lời: vì sao “tự chạy prettier sau mỗi lần sửa” nên là HOOK chứ không phải một dòng trong
CLAUDE.md?Hoàn thành khi:
CLAUDE.mdchỉ là lời khuyên Claude có thể quên; hookPostToolUsechạy chắc chắn mỗi lần → kết quả nhất quán. - 2
Hook tự format
Thêm hook
PostToolUsechoWrite|Editchạy formatter của dự án bạn (vd prettier/black) trên file vừa sửa. Sửa thử một file.Hoàn thành khi: Sau mỗi lần Claude sửa file, formatter tự chạy; bạn không phải nhắc “format lại giúp”.
- 3
Chặn lệnh nguy hiểm
Viết một hook
PreToolUsetrênBashdùng if "Bash(rm *)" để chặn (hoặc cảnh báo) lệnh xoá. Thử nhờ Claude chạyrm.Hoàn thành khi: Lệnh
rmbị chặn (mã thoát 2 hoặcpermissionDecision: deny); bạn thấy lý do hiện ra. - 4
Đọc dữ liệu sự kiện
Viết một script hook in ra (vd ghi log) trường
tool_namevàtool_inputnhận từ stdin để xem hook “thấy” gì.Hoàn thành khi: Bạn quan sát được JSON sự kiện (
session_id,tool_name,tool_input…) mà Claude Code truyền cho hook. - 5
Xem hook đang bật
Gõ
/hooksđể liệt kê hook đang hoạt động và nguồn của chúng (dự án/cá nhân/plugin).Hoàn thành khi: Bạn thấy danh sách hook + nguồn; biết hook nào áp cho phiên hiện tại.