Công cụ AI Coding

Hooks trong Claude Code là gì? Ví dụ & khi nào dùng (2026)

Aug 14, 202614 phút đọc

Claude Code hooks là các lệnh shell (hoặc HTTP/MCP) do bạn tự cấu hình trong settings.json, tự động chạy tại các thời điểm trong vòng đời phiên làm việc của Claude Code - ví dụ trước khi chạy tool, sau khi sửa file, hay khi Claude trả lời xong. Ba event lõi người mới cần biết là PreToolUse (chặn được lệnh nguy hiểm), PostToolUse (tự format code), và Stop (thông báo hoàn tất). Vì hook chạy với toàn quyền user và không có sandbox, hãy review kỹ trước khi bật.

Claude Code phát hành nhanh, danh sách event có thể được bổ sung liên tục.

Hooks trong Claude Code là gì?

Hooks trong Claude Code là những lệnh do bạn định nghĩa sẵn, được Claude Code tự động thực thi tại các điểm cố định trong vòng đời (lifecycle) của một phiên làm việc. Thay vì "nhắc" Claude làm gì đó và hy vọng nó nhớ, hook biến hành vi thành thứ chạy tất định (deterministic): đến đúng thời điểm là chạy, không phụ thuộc vào việc mô hình có "muốn" hay không.

Nếu bạn từng dùng Git hooks (như pre-commit chạy linter trước mỗi commit), khái niệm này rất quen thuộc - chỉ khác là hook ở đây gắn vào vòng đời của một AI coding agent thay vì vào Git. Khi Claude chuẩn bị chạy một tool, vừa sửa xong một file, hay kết thúc lượt trả lời, Claude Code sẽ kiểm tra xem có hook nào được đăng ký cho sự kiện đó không và chạy nó.

Điểm mạnh nhất của hook là tính tất định và có thể chặn. Một câu ghi trong CLAUDE.md kiểu "nhớ chạy Prettier sau khi sửa file" chỉ là gợi ý - mô hình có thể quên. Nhưng một PostToolUse hook thì luôn chạy Prettier, mỗi lần, không ngoại lệ. Với PreToolUse, hook còn có thể từ chối một hành động trước khi nó xảy ra - ví dụ chặn lệnh rm -rf nguy hiểm.

Đây là công cụ cốt lõi để tự động hóa Claude Code: format code, chạy test, log lại hoạt động, gửi thông báo, hay dựng hàng rào an toàn. Trong bài này mình đi qua cách hook hoạt động qua settings.json, ba ví dụ copy-paste được, khi nào nên (và không nên) dùng, cùng một mục an toàn mà đa số bài tiếng Việt bỏ qua.

Hooks hoạt động thế nào? (settings.json)

Hook được khai báo trong file settings.json dưới key hooks. Cấu trúc lồng theo ba tầng: tên event → danh sách matcher → danh sách hook cần chạy. Cụ thể là hooks > EventName > [{ matcher, hooks: [{ type, command }] }]. Đây là một cấu hình settings.json tối giản chạy được thật:

{
 "hooks": {
 "PostToolUse": [
 {
 "matcher": "Edit|Write",
 "hooks": [
 {
 "type": "command",
 "command": "npx prettier --write \"$CLAUDE_FILE_PATHS\""
 }
 ]
 }
 ]
 }
}

Đọc từ trong ra ngoài: khi event PostToolUse xảy ra, Claude Code so matcher (Edit|Write) với tên tool vừa chạy; nếu khớp, nó thực thi từng command trong mảng hooks.

Ba phạm vi cấu hình - quan trọng vì nó quyết định hook áp dụng ở đâu và có bị commit lên Git hay không:

FilePhạm viGitDùng cho
~/.claude/settings.jsonToàn máy (global)Không commitHook cá nhân dùng cho mọi project
.claude/settings.jsonTheo projectCommit được, chia sẻ cả teamHook chung của dự án (format, test)
.claude/settings.local.jsonTheo project, riêng bạnGitignored (mặc định)Hook nhạy cảm, có token/đường dẫn riêng

Ngoài ra plugin có thể mang theo hook riêng qua file hooks/hooks.json của nó.

Trường type hỗ trợ năm loại: command (chạy lệnh shell - phổ biến nhất), http (gọi một URL), mcp_tool (gọi tool của một MCP server - xem thêm MCP là gì và dùng thế nào), prompt, và agent. Trong toàn bộ bài này mình tập trung vào command vì nó copy-paste chạy được ngay và đủ cho 90% nhu cầu. (Tham số settings.json hooks đúng cú pháp là điều kiện tiên quyết - sai JSON là hook im lặng không chạy.)

Các loại event chính (lifecycle)

Bạn không cần thuộc lòng hết. Với người mới, chỉ vài event dưới đây là đủ dùng cho phần lớn tình huống:

EventKích hoạt khiChặn được?Dùng điển hình
PreToolUseTrước khi Claude chạy một toolChặn lệnh nguy hiểm, yêu cầu xác nhận
PostToolUseSau khi tool chạy thành côngKhôngFormat code, chạy test, log
UserPromptSubmitKhi bạn gửi một promptChèn context, kiểm tra đầu vào
StopKhi Claude trả lời xong lượtKhôngGửi thông báo hoàn tất
SessionStartKhi mở phiên mớiKhôngNạp biến môi trường, ghi log
NotificationKhi Claude phát thông báoKhôngChuyển thông báo sang kênh khác

Lưu ý cập nhật 2026 (info-gain): nhiều bài tiếng Việt vẫn chỉ liệt kê 4 event cũ. Thực tế Claude Code hiện có hơn 30 lifecycle event, bổ sung những cái như PostToolUseFailure, SubagentStart/SubagentStop, PreCompact/PostCompact, SessionEnd… theo tài liệu chính thức của Anthropic (code.claude.com/docs/en/hooks, truy cập 2026-08-09). Nhưng đừng lo - người mới chỉ cần nắm 3-4 event lõi ở trên, phần còn lại là dành cho kịch bản nâng cao.

Matcher quyết định hook áp dụng cho tool nào. Bốn dạng thường gặp: khớp chính xác một tool ("Bash"), khớp nhiều tool bằng dấu gạch đứng ("Edit|Write"), regex ("mcp__.*" để bắt mọi tool MCP), và để trống hoặc "*" nghĩa là khớp tất cả. So sánh sâu hơn PreToolUsePostToolUse qua ví dụ ngay dưới đây.

Ví dụ 1 - PreToolUse: chặn lệnh nguy hiểm

Đây là use case "wow" nhất và cũng là hàng rào an toàn thực tế nhất. Ý tưởng: trước khi Claude chạy bất kỳ lệnh Bash nào, một hook kiểm tra nội dung lệnh; nếu phát hiện mẫu nguy hiểm như rm -rf, hook từ chối và không cho lệnh chạy.

PreToolUse có hai cách chặn. Cách "sạch" là in ra JSON với permissionDecision nhận một trong ba giá trị: "allow" (cho chạy, bỏ qua bước hỏi), "deny" (chặn hẳn), "ask" (buộc hỏi bạn). Cách nhanh gọn là dùng exit code: script thoát với exit 2 để chặn - khi đó nội dung stderr được gửi ngược lại cho Claude để nó biết vì sao bị chặn. Cấu hình:

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

Và script .claude/hooks/block-danger.sh:

#!/usr/bin/env bash
# Đọc payload JSON từ stdin, lấy lệnh Claude định chạy
input=$(cat)
command=$(echo "$input" | jq -r '.tool_input.command // empty')

if echo "$command" | grep -Eq 'rm[[:space:]]+-rf|git[[:space:]]+push[[:space:]]+--force'; then
 echo "Bị chặn: lệnh có mẫu nguy hiểm ($command)" >&2
 exit 2 # exit 2 = chặn, stderr gửi lại cho Claude
fi
exit 0 # exit 0 = cho phép chạy tiếp

Kết quả: khi Claude định gõ rm -rf build/, hook bắt được, trả exit 2, lệnh không chạy, và Claude nhận thông điệp giải thích. Đây là thứ mà CLAUDE.md rule không đảm bảo được - vì rule chỉ là gợi ý mềm, còn hook là hàng rào cứng. Nếu bạn muốn siết quyền ở tầng cao hơn, đọc thêm permissions và cấu hình an toàn trong Claude Code.

Ví dụ 2 - PostToolUse: tự động format code

Một trong những hook mình bật đầu tiên ở mọi project: tự chạy formatter sau mỗi lần Claude sửa file. Không còn cảnh diff lem nhem vì thiếu dấu cách hay xuống dòng sai. Vì đây là PostToolUse (chạy sau khi tool thành công), nó không chặn gì cả - chỉ dọn dẹp.

{
 "hooks": {
 "PostToolUse": [
 {
 "matcher": "Edit|Write",
 "hooks": [
 {
 "type": "command",
 "command": "cd \"${CLAUDE_PROJECT_DIR}\" && npx prettier --write \"$CLAUDE_FILE_PATHS\""
 }
 ]
 }
 ]
 }
}

Matcher Edit|Write bắt cả hai tool sửa/ghi file. Với dự án Python, đổi command thành black "$CLAUDE_FILE_PATHS" hoặc ruff format. Vài biến môi trường hữu ích Claude Code truyền vào hook:

  • ${CLAUDE_PROJECT_DIR} - đường dẫn tuyệt đối gốc dự án, để lệnh chạy đúng thư mục.
  • $CLAUDE_FILE_PATHS - đường dẫn (các) file vừa bị tác động, để format đúng file thay vì cả repo.
  • Ngoài ra bạn luôn có thể đọc payload JSON đầy đủ từ stdin (như ví dụ 1) để lấy chi tiết tool_input.

Mẹo: giữ command nhẹ và nhanh. Hook PostToolUse chạy sau mỗi lần sửa file, nên một formatter chậm sẽ làm cả phiên ì ạch.

Ví dụ 3 - Stop: thông báo khi Claude làm xong

Khi giao cho Claude một tác vụ dài rồi chuyển sang việc khác, bạn dễ quên ngó lại. Hook Stop chạy khi Claude kết thúc lượt trả lời - dùng để bắn một thông báo. Ví dụ dùng ntfy để đẩy sang điện thoại:

{
 "hooks": {
 "Stop": [
 {
 "hooks": [
 {
 "type": "command",
 "command": "curl -s -d \"Claude Code đã xong tác vụ\" ntfy.sh/ten-topic-cua-ban"
 }
 ]
 }
 ]
 }
}

Không cần matcherStop không gắn với tool nào. Trên macOS bạn có thể thay bằng osascript -e 'display notification "Xong!" with title "Claude Code"'; trên Linux dùng notify-send. Nhỏ mà cực tiện khi làm nhiều việc song song.

Khi nào nên - và KHÔNG nên - dùng hook

Hook mạnh, nhưng không phải công cụ cho mọi thứ. Ranh giới đơn giản: hook dành cho việc luôn phải chạy và mang tính tất định - format, test, chặn lệnh, ghi log. Nếu thứ bạn cần là hướng dẫn hành vi hay một năng lực, có công cụ phù hợp hơn.

Vấn đề bạn muốn giảiCông cụ đúng
Việc phải chạy mỗi lần, tất định (format, test, chặn lệnh)Hook
Định hướng phong cách/quy ước code cho ClaudeRule trong CLAUDE.md
Một hành động bạn tự gọi khi cầnSlash command trong Claude Code
Đóng gói một năng lực tái dùng (kèm hướng dẫn + script)Skill trong Claude Code

Ví dụ dễ nhầm: "nhắc Claude luôn viết test" nên là rule trong CLAUDE.md, không phải hook - vì đó là định hướng mềm. Còn "chạy toàn bộ test suite sau khi sửa file src/" thì đúng là hook, vì nó tất định. Nếu vẫn thấy rối bốn khái niệm này, mình có bài riêng phân biệt skills, subagents, hooks và MCP - đây là nơi tổng hợp toàn cảnh.

⚠️ Lưu ý an toàn khi dùng hook

Đây là phần đa số bài tiếng Việt bỏ qua, nhưng lại quan trọng nhất. Theo tài liệu chính thức (code.claude.com/docs/en/hooks, truy cập 2026-08-09): hook chạy với toàn quyền của tài khoản user và KHÔNG có sandbox. Nghĩa là một hook viết sai - hay một hook độc hại bạn vô tình copy về - có thể xóa file, làm lộ secret, hoặc chạy code tùy ý trên máy bạn, hoàn toàn tự động và không hỏi lại.

Vài nguyên tắc mình luôn tuân thủ:

  • Đọc kỹ mọi hook trước khi bật - đặc biệt hook đến từ plugin, kit, hay repo của người khác. Coi nó như code chạy root.
  • Không hardcode secret (token, API key) trong command. Đọc từ biến môi trường thay vì viết thẳng.
  • Hook nhạy cảm để trong .claude/settings.local.json (gitignored) để không vô tình commit lên repo chung.
  • Biết cách tắt khẩn cấp: dùng disableAllHooks để vô hiệu toàn bộ hook khi debug hay khi nghi ngờ. Doanh nghiệp có thể siết thêm bằng allowManagedHooksOnlyallowedHttpHookUrls.
  • Cẩn trọng với PreToolUse tự động allow - nó bỏ qua bước xác nhận, tiện nhưng cũng gỡ mất một lớp bảo vệ.

Tóm lại: hook là dao sắc. Rất hữu ích, nhưng phải cầm đúng cách. Xem thêm các lỗi hay gặp và cách xử lý trong bài khắc phục lỗi Claude Code thường gặp.

Tăng tốc: hook & skill dựng sẵn từ bộ kit

Tự viết hook, script và skill cho từng project khá tốn công - nhất là khi bạn muốn một bộ hàng rào an toàn và workflow nhất quán. Một số bộ kit cho Claude Code như bộ kit AgentKit cho Claude Code đóng gói sẵn skills, subagents và workflow để bạn khỏi dựng lại từ đầu. Nếu muốn xem trực tiếp, có thể xem giá AgentKit (giảm 20% qua link). Nói thẳng: hook cơ bản trong bài này bạn tự làm được và không cần mua gì; kit chỉ đáng cân nhắc khi bạn cần cả một bộ dựng sẵn.

Debug: vì sao hook không chạy?

Hook "im lặng" là lỗi phổ biến nhất. Đi qua checklist này gần như luôn tìm ra nguyên nhân:

  • JSON có hợp lệ không? Một dấu phẩy thừa trong settings.json là hook cả file không nạp. Chạy qua một trình validate JSON.
  • Matcher có đúng tên tool không? Tên phân biệt hoa thường: là Bash, Edit, Write - không phải bash hay edit.
  • Script trả exit code đúng chưa? exit 0 để cho qua, exit 2 để chặn (với PreToolUse). Exit code khác có thể bị bỏ qua.
  • stdout có "sạch" không? Nếu hook trả JSON điều khiển, stdout chỉ được chứa JSON đó - text thừa sẽ làm parse lỗi.
  • File script có quyền chạy? Trên macOS/Linux nhớ chmod +x.
  • disableAllHooks có đang bật? Nếu trước đó bạn tắt để debug, đừng quên bật lại.

Câu hỏi thường gặp (FAQ)

Hook khác slash command và skill thế nào?

Hook chạy tự động tại các điểm trong vòng đời (bạn không gọi tay). Slash command là hành động bạn tự gọi khi cần. Skill là một năng lực đóng gói (hướng dẫn + script) mà Claude tự nạp khi phù hợp ngữ cảnh. Nói ngắn: hook = tự động và tất định, slash command = thủ công, skill = năng lực tái dùng.

Hook chạy trên Windows được không?

Được. Vì type: command chạy lệnh shell, bạn có thể trỏ tới script PowerShell (powershell -File .claude\hooks\block-danger.ps1) hoặc dùng Git Bash/WSL để chạy script bash. Chỉ cần command hợp lệ trên shell máy bạn.

Hook có làm chậm Claude Code không?

Có, nếu command nặng. Hook chạy đồng bộ tại thời điểm event, nên một formatter hay test suite chậm sẽ kéo dài mỗi lượt. Giữ hook nhẹ, chỉ format file vừa đổi ($CLAUDE_FILE_PATHS) thay vì cả repo, và cân nhắc bỏ test nặng ra khỏi PostToolUse.

Hook global và project khác gì nhau?

Hook global (~/.claude/settings.json) áp dụng cho mọi project trên máy bạn và không được commit. Hook project (.claude/settings.json) chỉ áp dụng cho dự án đó và commit được để cả team dùng chung. Bản .local.json theo project nhưng gitignored, dành cho cấu hình riêng bạn.

Chặn lệnh nguy hiểm bằng hook được không?

Được, đây chính là điểm mạnh của PreToolUse. Hook kiểm tra lệnh trước khi chạy và từ chối bằng permissionDecision: "deny" hoặc exit code 2 - ví dụ chặn rm -rf hay git push --force (xem Ví dụ 1 phía trên).

Hook có an toàn không?

Hook chạy với toàn quyền user và không có sandbox, nên một hook sai hoặc độc hại có thể gây hại thật (xóa file, lộ secret). Bản thân cơ chế an toàn nếu bạn tự viết và review kỹ; rủi ro đến từ việc bật hook lạ mà không đọc. Luôn coi hook như code chạy với quyền cao nhất.

Kết luận + bước tiếp theo

Nắm ba event lõi là bạn đã dùng được hook cho hầu hết nhu cầu: PreToolUse để chặn lệnh nguy hiểm, PostToolUse để tự format/test, và Stop để nhận thông báo. Nhớ nguyên tắc vàng: hook chạy toàn quyền và không sandbox - review kỹ trước khi bật. Đi tiếp, đọc skill trong Claude Code là gìslash commands trong Claude Code, hoặc xem bức tranh tổng để phân biệt skills, subagents, hooks và MCP khi nào dùng cái nào.

J

Jasmine

Tác giả · Jasmine Daily

Người viết nên Jasmine Daily - ghi lại những suy nghĩ, trải nghiệm và những khoảnh khắc đời thường. Thật lòng, không vội vàng, không hoàn hảo.

Jasmine Daily

Vẫn còn nhiều điều đang chờ được đọc.

Nếu bài viết này chạm đến bạn, hãy ghé xem thêm vài trang khác trong cuốn nhật ký này.

Đọc tiếp

Bài viết liên quan