Bỏ qua để đến nội dung

Hooks

Hooks là lệnh shell do bạn định nghĩa, chạy tự động tại các thời điểm cố định trong vòng đời của Claude Code — ví dụ tự format code sau mỗi lần sửa file, hoặc chặn một lệnh nguy hiểm trước khi nó kịp chạy. Điểm khác biệt so với việc chỉ dặn dò trong CLAUDE.md: hooks luôn chạy đúng như cấu hình, không phụ thuộc vào việc model có “nhớ” hay quyết định làm theo hay không — xem thêm ở Khi nào dùng Hooks thay vì CLAUDE.md bên dưới.

Đây là các sự kiện phổ biến nhất — danh sách đầy đủ còn nhiều hơn (khoảng 30 sự kiện, bao gồm cả sự kiện cho subagent, MCP, worktree…):

Sự kiện Thời điểm chạy
PreToolUse Trước khi Claude gọi một tool — có thể chặn hành động
PostToolUse Sau khi tool chạy xong thành công
UserPromptSubmit Khi bạn gửi một prompt mới, trước khi Claude xử lý
Notification Khi Claude Code gửi thông báo (ví dụ đang chờ bạn phản hồi)
SessionStart Khi một phiên bắt đầu hoặc được resume
Stop Khi Claude kết thúc lượt trả lời

Ví dụ 1: tự động format sau khi sửa file

Phần tiêu đề “Ví dụ 1: tự động format sau khi sửa file”

Chạy Prettier trên mọi file Claude vừa sửa, để format luôn nhất quán mà không cần bạn can thiệp tay. Thêm vào .claude/settings.json ở gốc dự án:

{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.file_path' | xargs npx prettier --write"
}
]
}
]
}
}

matcher: "Edit|Write" nghĩa là hook này chỉ chạy khi Claude dùng tool sửa/tạo file, không chạy với Bash, Read… Lệnh dùng jq để lấy đường dẫn file từ dữ liệu JSON mà Claude Code truyền vào qua stdin.

Ngăn Claude sửa các file như .env, package-lock.json, hay bất cứ thứ gì trong .git/. Tạo script .claude/hooks/protect-files.sh:

#!/bin/bash
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
PROTECTED=(".env" "package-lock.json" ".git/")
for pattern in "${PROTECTED[@]}"; do
if [[ "$FILE_PATH" == *"$pattern"* ]]; then
echo "Blocked: $FILE_PATH khớp mẫu bị chặn '$pattern'" >&2
exit 2
fi
done
exit 0

Nhớ cấp quyền thực thi: chmod +x .claude/hooks/protect-files.sh. Sau đó đăng ký hook trong .claude/settings.json:

{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{ "type": "command", "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/protect-files.sh" }
]
}
]
}
}

Hook nhận dữ liệu sự kiện dạng JSON qua stdin, và báo lại quyết định qua exit code:

  • Exit 0 — không phản đối, hành động tiếp tục diễn ra bình thường (với PreToolUse, vẫn phải qua luồng xin phép thông thường nếu có).
  • Exit 2chặn hành động. Ghi lý do ra stderr, Claude sẽ đọc được lý do đó và tự điều chỉnh cách làm.
  • Mã khác — hành động vẫn tiếp tục, nhưng transcript hiện một cảnh báo lỗi hook.

Với nhu cầu phức tạp hơn (ví dụ tự động cho phép một hành động cụ thể thay vì chỉ chặn/không chặn), hook có thể in ra JSON có cấu trúc ở stdout thay vì chỉ dùng exit code — xem chi tiết trong tài liệu gốc ở phần Nguồn.

  • /hooks — xem toàn bộ hook đang cấu hình, nhóm theo sự kiện (chỉ xem, muốn sửa phải chỉnh trực tiếp file settings hoặc nhờ Claude sửa giúp).
  • Đặt "disableAllHooks": true trong settings để tắt tạm toàn bộ hooks (trừ hook ở cấp cấu hình tổ chức).

Vị trí đặt hook quyết định phạm vi áp dụng — giống với settings.json nói chung (xem thêm): ~/.claude/settings.json cho mọi dự án của bạn, .claude/settings.json để chia sẻ với cả team qua git, .claude/settings.local.json chỉ cho máy này.

  • CLAUDE.md: chỉ dẫn bằng ngôn ngữ tự nhiên, Claude cố gắng làm theo nhưng không đảm bảo 100% — phù hợp cho quy ước, ngữ cảnh, sở thích chung.
  • Hooks: thực thi bắt buộc bằng shell script, luôn chạy đúng như cấu hình, không phụ thuộc vào việc model có tuân thủ hay không — phù hợp cho yêu cầu bắt buộc phải xảy ra (chạy lint trước commit, chặn thao tác nguy hiểm, ghi log audit…).

Dùng hooks khi hậu quả của việc Claude “quên” là không chấp nhận được; dùng CLAUDE.md cho phần còn lại.