TL;DR

Claude Code hooks chia thành 27+ lifecycle events, được nhóm theo 3 nhịp: một lần mỗi session, một lần mỗi turn, và mỗi tool call. PreToolUse là event quan trọng nhất - nó có thể chặn tool call với 4 outcomes. Tuy nhiên hooks có giới hạn rõ ràng: không phải security sandbox, không thể cách ly đọc file tuyệt đối, và command hooks chạy với full user permissions.

Ba nhịp của Claude Code lifecycle

Tài liệu chính thức của Anthropic chia hooks thành 3 nhịp kích hoạt:

  • Một lần mỗi session: SessionStart, SessionEnd, Setup
  • Một lần mỗi turn: UserPromptSubmit, UserPromptExpansion, Stop, StopFailure
  • Mỗi tool call: PreToolUse, PostToolUse, PostToolUseFailure, PostToolBatch, PermissionRequest, PermissionDenied

Ngoài ra còn có các events chuyên biệt hơn: SubagentStart, SubagentStop, TaskCreated, TaskCompleted, TeammateIdle, FileChanged, CwdChanged, ConfigChange, InstructionsLoaded, WorktreeCreate, WorktreeRemove, PreCompact, PostCompact, Notification, Elicitation, ElicitationResult.

Tổng cộng hơn 27 điểm có thể kéo hook vào - nhiều hơn phần lớn mọi người tưởng.

Hook execution pipeline: Event fires → Matcher checks → Handler runs

PreToolUse - event quan trọng nhất để kiểm soát

PreToolUse chạy sau khi Claude tạo ra tool parameters nhưng trước khi tool thực sự thực thi. Đây là điểm duy nhất bạn có thể can thiệp và thay đổi kết quả của một tool call.

Event này hỗ trợ 4 outcomes qua field permissionDecision:

  • "allow" - bỏ qua hộp thoại xác nhận, cho phép chạy ngay
  • "deny" - chặn tool call hoàn toàn, AI nhận được lý do
  • "ask" - bắt buộc hiện hộp thoại cho người dùng xác nhận
  • "defer" - thoát gracefully để tool có thể được resume sau

Ví dụ thực tế: khi Claude chuẩn bị chạy Bash "rm -rf /tmp/build", hook PreToolUse với matcher "Bash" sẽ nhận JSON input qua stdin, kiểm tra command, phát hiện rm -rf, trả về {"hookSpecificOutput": {"permissionDecision": "deny", "permissionDecisionReason": "Destructive command blocked"}}. Claude Code hủy tool call và thông báo cho AI.

Điều quan trọng: PreToolUse nhận tool_nametool_input - bao gồm tất cả parameters. Bạn có thể match trên tên tool (Bash, Edit, Write, Read, Glob, Grep, Agent, WebFetch...) hoặc dùng điều kiện if để lọc chi tiết hơn trong parameters.

Các events khác đáng biết

SessionStart - chạy mỗi khi session bắt đầu. Dùng để inject context, load environment variables, hoặc kéo lịch sử từ database vào. Plugin claude-mem dùng event này để inject memory từ các session trước.

Stop - chạy khi Claude hoàn thành một turn. Có thể chặn Claude dừng lại nếu điều kiện chưa thỏa mãn - ví dụ: bắt Claude phải chạy test suite trước khi cho phép kết thúc bằng cách return exit code 2 kèm lý do.

PostToolUse - chạy sau khi tool thực thi thành công. Không thể undo những gì tool đã làm, nhưng có thể quan sát, log, và trả context bổ sung cho Claude. Plugin claude-mem dùng event này để ghi lại mọi hành động của AI.

UserPromptSubmit - chạy khi bạn submit prompt, trước khi Claude xử lý. Cho phép validate prompt, inject thêm context ẩn, hoặc chặn các loại prompt nhất định.

SubagentStart / SubagentStop - khi Claude spawn hoặc kết thúc một subagent. Hữu ích để inject context vào subagent hoặc enforce quality gates trước khi subagent kết thúc.

Giới hạn thực sự của hooks

Đây là phần hay bị bỏ qua nhất nhưng quan trọng nhất. Hooks không phải security sandbox.

Những gì hooks làm tốt: Chặn write operations và destructive commands qua PreToolUse. Lý do: "ghi file" là hành động rõ ràng, có ít đường vào, bạn có thể canh được.

Những gì hooks không thể ngăn tuyệt đối: Chặn đọc file nhạy cảm. Nếu bạn block tool Read, AI vẫn có thể dùng Bash để cat, head, tail, hay thậm chí viết một script Python một dòng để đọc. Bịt được lỗ này thì phải bịt cả Bash, rồi lại rơi vào cuộc chiến regex không hồi kết: cat bịt rồi thì còn less, xxd, python -c "open(...)"...

Ranh giới an toàn đúng: Hooks là hàng phòng thủ cuối cùng, không phải đầu tiên. Để bảo vệ file nhạy cảm thực sự: đặt chúng vào environment variables hoặc secret manager thay vì để plain text trong project, dùng permission rules và sandbox để chặn ở cấp OS, còn hooks đóng vai trò alert khi có gì đó vượt qua được.

Command hooks chạy với full system permissions: Hook script của bạn có thể đọc, ghi, xóa bất kỳ file nào user account có quyền. Không có sandbox. Nếu ai đó inject malicious content vào output của AI để trigger hook của bạn chạy một lệnh nguy hiểm, hook đó sẽ chạy với quyền của bạn. Luôn validate và sanitize input trong hook scripts.

Một số events không có decision control

Không phải event nào cũng có thể bị chặn. Bảng nhanh:

EventCó thể chặn?Ghi chú
PreToolUseChặn tool call
PermissionRequestAllow/deny permission dialog
UserPromptSubmitChặn prompt
StopBuộc Claude tiếp tục
SessionStartKhôngChỉ inject context
PostToolUseKhôngTool đã chạy xong
NotificationKhôngChỉ side effects
FileChangedKhôngChỉ observe

Lưu ý đặc biệt: policy_settings (managed settings của enterprise) không thể bị chặn dù hook có fire - system đảm bảo enterprise settings luôn có hiệu lực.

Kết - Hooks là layer, không phải toàn bộ kiến trúc

Hiểu đúng về hooks giúp bạn tránh hai cực đoan: không dùng vì nghĩ quá phức tạp, hay dùng quá nhiều và kỳ vọng hooks giải quyết mọi vấn đề bảo mật.

Hooks là layer kiểm soát nằm trong toolchain. Chúng làm tốt nhất khi được thiết kế đúng mục đích: chặn write operations nguy hiểm, observe AI behavior, inject context tự động, và trigger automation. Phần còn lại - bảo vệ secrets, cách ly môi trường - cần các lớp khác.

Bài cuối trong series sẽ xem xét claude-mem - plugin dùng hooks để giải quyết vấn đề "AI không có trí nhớ" và pattern thiết kế "fast hook + slow worker" mà bất kỳ ai làm hooks phức tạp đều nên biết.

via Claude Code Hooks Reference