{"product_id":"claude-code-hooks-tự-dộng-hoa-workflow-với-pre-post-hooks","title":"Claude Code Hooks — Tự động hóa workflow với pre\/post hooks","description":"\n\u003cp\u003eHooks trong Claude Code là cơ chế cho phép bạn chạy các lệnh shell tự động trước hoặc sau khi Claude thực hiện một hành động cụ thể. Tương tự như git hooks cho phép chạy script trước\/sau commit, Claude Code hooks cho phép bạn tự động hóa các bước trong workflow phát triển — từ format code, chạy linter, thực thi test đến gửi thông báo. Đây là tính năng nâng cao giúp biến Claude Code từ một công cụ hỗ trợ thành một phần tích hợp chặt chẽ trong quy trình phát triển phần mềm của bạn.\u003c\/p\u003e\n\n\u003ch2\u003eHooks là gì và tại sao cần chúng?\u003c\/h2\u003e\n\u003cp\u003eKhi làm việc với Claude Code, có những tác vụ bạn muốn xảy ra tự động mỗi khi Claude thực hiện một hành động nhất định. Ví dụ:\u003c\/p\u003e\n\u003cul\u003e\n  \u003cli\u003eTự động chạy Prettier hoặc ESLint sau khi Claude sửa file JavaScript\u003c\/li\u003e\n  \u003cli\u003eChạy unit test sau khi Claude thay đổi code trong thư mục src\/\u003c\/li\u003e\n  \u003cli\u003eGửi thông báo đến Slack khi Claude hoàn thành một tác vụ lớn\u003c\/li\u003e\n  \u003cli\u003eKiểm tra type safety với TypeScript compiler trước khi Claude commit\u003c\/li\u003e\n  \u003cli\u003eTự động backup file trước khi Claude ghi đè\u003c\/li\u003e\n\u003c\/ul\u003e\n\u003cp\u003eKhông có hooks, bạn phải nhớ chạy từng lệnh thủ công hoặc nhắc Claude làm mỗi lần. Hooks giải quyết vấn đề này bằng cách tự động hóa — đảm bảo các bước quan trọng không bao giờ bị bỏ qua.\u003c\/p\u003e\n\n\u003ch3\u003eBa loại hooks trong Claude Code\u003c\/h3\u003e\n\u003cp\u003eClaude Code hỗ trợ ba loại hooks, mỗi loại kích hoạt tại thời điểm khác nhau trong quá trình thực thi:\u003c\/p\u003e\n\u003cul\u003e\n  \u003cli\u003e\n\u003cstrong\u003ePreToolUse:\u003c\/strong\u003e Chạy trước khi Claude sử dụng một tool cụ thể (ví dụ: trước khi ghi file, trước khi chạy lệnh bash). Có thể chặn hành động nếu hook thất bại\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003ePostToolUse:\u003c\/strong\u003e Chạy sau khi Claude sử dụng tool thành công. Phù hợp cho validation, formatting, testing\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003eNotification:\u003c\/strong\u003e Chạy khi Claude gửi thông báo (ví dụ: khi cần input từ người dùng, khi hoàn thành tác vụ). Phù hợp cho tích hợp với hệ thống thông báo\u003c\/li\u003e\n\u003c\/ul\u003e\n\n\u003ch2\u003eCấu hình hooks trong settings.json\u003c\/h2\u003e\n\u003cp\u003eHooks được cấu hình trong file settings.json của Claude Code. File này có thể nằm ở cấp project hoặc cấp user:\u003c\/p\u003e\n\u003cul\u003e\n  \u003cli\u003e\n\u003cstrong\u003eProject-level:\u003c\/strong\u003e .claude\/settings.json trong thư mục dự án — áp dụng cho tất cả người dùng làm việc trên project\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003eUser-level:\u003c\/strong\u003e ~\/.claude\/settings.json — áp dụng cho mọi project của bạn\u003c\/li\u003e\n\u003c\/ul\u003e\n\n\u003ch3\u003eCấu trúc cơ bản của hooks\u003c\/h3\u003e\n\u003cpre\u003e\u003ccode\u003e{\n  \"hooks\": {\n    \"PreToolUse\": [\n      {\n        \"matcher\": \"Write\",\n        \"hooks\": [\n          {\n            \"type\": \"command\",\n            \"command\": \"echo 'About to write a file'\"\n          }\n        ]\n      }\n    ],\n    \"PostToolUse\": [\n      {\n        \"matcher\": \"Write\",\n        \"hooks\": [\n          {\n            \"type\": \"command\",\n            \"command\": \"npx prettier --write $CLAUDE_FILE_PATH\"\n          }\n        ]\n      }\n    ],\n    \"Notification\": [\n      {\n        \"matcher\": \"\",\n        \"hooks\": [\n          {\n            \"type\": \"command\",\n            \"command\": \"echo '$CLAUDE_NOTIFICATION' \u0026gt;\u0026gt; ~\/claude-notifications.log\"\n          }\n        ]\n      }\n    ]\n  }\n}\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003cp\u003eGiải thích cấu trúc:\u003c\/p\u003e\n\u003cul\u003e\n  \u003cli\u003e\n\u003cstrong\u003ematcher:\u003c\/strong\u003e Tên tool cần hook vào. Các tool phổ biến: Write (ghi file), Bash (chạy lệnh), Read (đọc file). Để trống \"\" để match tất cả tools\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003ehooks:\u003c\/strong\u003e Mảng các hook cần chạy. Mỗi hook có type (\"command\") và command (lệnh shell cần thực thi)\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003eBiến môi trường:\u003c\/strong\u003e Claude Code truyền các biến như $CLAUDE_FILE_PATH, $CLAUDE_NOTIFICATION vào hook command\u003c\/li\u003e\n\u003c\/ul\u003e\n\n\u003ch2\u003eCác biến môi trường có sẵn trong hooks\u003c\/h2\u003e\n\u003cp\u003eKhi hook chạy, Claude Code cung cấp thông tin ngữ cảnh qua biến môi trường. Dưới đây là các biến quan trọng nhất:\u003c\/p\u003e\n\u003cul\u003e\n  \u003cli\u003e\n\u003cstrong\u003e$CLAUDE_FILE_PATH:\u003c\/strong\u003e Đường dẫn file đang được thao tác (với Write, Read tools)\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003e$CLAUDE_TOOL_NAME:\u003c\/strong\u003e Tên tool đang được sử dụng\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003e$CLAUDE_TOOL_INPUT:\u003c\/strong\u003e Input JSON đầy đủ của tool (chứa tất cả tham số)\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003e$CLAUDE_NOTIFICATION:\u003c\/strong\u003e Nội dung thông báo (với Notification hooks)\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003e$CLAUDE_PROJECT_DIR:\u003c\/strong\u003e Thư mục gốc của project\u003c\/li\u003e\n\u003c\/ul\u003e\n\n\u003ch2\u003eUse case 1: Auto-format code sau khi Claude viết file\u003c\/h2\u003e\n\u003cp\u003eĐây là use case phổ biến nhất. Claude viết code theo style riêng, nhưng project của bạn có quy chuẩn format nhất định qua Prettier, Black, gofmt hoặc công cụ tương tự.\u003c\/p\u003e\n\n\u003ch3\u003eCấu hình Prettier cho JavaScript\/TypeScript\u003c\/h3\u003e\n\u003cpre\u003e\u003ccode\u003e{\n  \"hooks\": {\n    \"PostToolUse\": [\n      {\n        \"matcher\": \"Write\",\n        \"hooks\": [\n          {\n            \"type\": \"command\",\n            \"command\": \"case \"$CLAUDE_FILE_PATH\" in *.js|*.ts|*.jsx|*.tsx|*.json|*.css) npx prettier --write \"$CLAUDE_FILE_PATH\" ;; esac\"\n          }\n        ]\n      }\n    ]\n  }\n}\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003cp\u003eHook này kiểm tra extension file — chỉ chạy Prettier cho các file JavaScript, TypeScript, JSON và CSS. Các file khác sẽ bị bỏ qua.\u003c\/p\u003e\n\n\u003ch3\u003eCấu hình cho dự án Python\u003c\/h3\u003e\n\u003cpre\u003e\u003ccode\u003e{\n  \"hooks\": {\n    \"PostToolUse\": [\n      {\n        \"matcher\": \"Write\",\n        \"hooks\": [\n          {\n            \"type\": \"command\",\n            \"command\": \"case \"$CLAUDE_FILE_PATH\" in *.py) black \"$CLAUDE_FILE_PATH\" \u0026amp;\u0026amp; isort \"$CLAUDE_FILE_PATH\" ;; esac\"\n          }\n        ]\n      }\n    ]\n  }\n}\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch2\u003eUse case 2: Lint trước khi commit\u003c\/h2\u003e\n\u003cp\u003eBạn có thể kết hợp hooks để đảm bảo code luôn qua lint trước khi Claude tạo commit.\u003c\/p\u003e\n\n\u003cpre\u003e\u003ccode\u003e{\n  \"hooks\": {\n    \"PreToolUse\": [\n      {\n        \"matcher\": \"Bash\",\n        \"hooks\": [\n          {\n            \"type\": \"command\",\n            \"command\": \"if echo \"$CLAUDE_TOOL_INPUT\" | grep -q 'git commit'; then npx eslint src\/ --max-warnings 0 || (echo 'ESLint failed. Fix errors before committing.' \u0026amp;\u0026amp; exit 1); fi\"\n          }\n        ]\n      }\n    ]\n  }\n}\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003cp\u003eHook này kiểm tra xem Claude có đang cố chạy git commit không. Nếu có, ESLint sẽ chạy trước. Nếu ESLint phát hiện lỗi, hook trả về exit code 1 và Claude sẽ không thực hiện commit.\u003c\/p\u003e\n\n\u003ch2\u003eUse case 3: Tự động chạy test sau khi sửa code\u003c\/h2\u003e\n\u003cp\u003eĐể đảm bảo Claude không vô tình phá vỡ functionality hiện có, bạn có thể cấu hình hook chạy test sau mỗi lần sửa file trong thư mục src\/.\u003c\/p\u003e\n\n\u003cpre\u003e\u003ccode\u003e{\n  \"hooks\": {\n    \"PostToolUse\": [\n      {\n        \"matcher\": \"Write\",\n        \"hooks\": [\n          {\n            \"type\": \"command\",\n            \"command\": \"if echo \"$CLAUDE_FILE_PATH\" | grep -q 'src\/'; then npm test -- --bail --silent 2\u0026gt;\u0026amp;1 | tail -20; fi\"\n          }\n        ]\n      }\n    ]\n  }\n}\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003cp\u003eLưu ý: Hook này chạy toàn bộ test suite, có thể chậm với project lớn. Trong thực tế, bạn có thể viết script thông minh hơn để chỉ chạy test liên quan đến file đã thay đổi.\u003c\/p\u003e\n\n\u003ch3\u003eScript chạy test thông minh\u003c\/h3\u003e\n\u003cp\u003eTạo file .claude\/scripts\/smart-test.sh:\u003c\/p\u003e\n\u003cpre\u003e\u003ccode\u003e#!\/bin\/bash\n# Tìm file test tương ứng với file source đã thay đổi\nSOURCE_FILE=\"$1\"\nTEST_FILE=$(echo \"$SOURCE_FILE\" | sed 's\/src\/\/test\/\/' | sed 's\/.ts$\/.test.ts\/')\n\nif [ -f \"$TEST_FILE\" ]; then\n  npx jest \"$TEST_FILE\" --bail --silent 2\u0026gt;\u0026amp;1 | tail -10\nelse\n  echo \"No corresponding test file found for $SOURCE_FILE\"\nfi\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003cp\u003eSau đó cấu hình hook:\u003c\/p\u003e\n\u003cpre\u003e\u003ccode\u003e{\n  \"hooks\": {\n    \"PostToolUse\": [\n      {\n        \"matcher\": \"Write\",\n        \"hooks\": [\n          {\n            \"type\": \"command\",\n            \"command\": \"if echo \"$CLAUDE_FILE_PATH\" | grep -q 'src\/'; then bash .claude\/scripts\/smart-test.sh \"$CLAUDE_FILE_PATH\"; fi\"\n          }\n        ]\n      }\n    ]\n  }\n}\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch2\u003eUse case 4: Thông báo khi hoàn thành\u003c\/h2\u003e\n\u003cp\u003eKhi Claude chạy tác vụ dài (refactor lớn, migration), bạn có thể muốn nhận thông báo khi hoàn thành.\u003c\/p\u003e\n\n\u003ch3\u003eThông báo qua macOS notification\u003c\/h3\u003e\n\u003cpre\u003e\u003ccode\u003e{\n  \"hooks\": {\n    \"Notification\": [\n      {\n        \"matcher\": \"\",\n        \"hooks\": [\n          {\n            \"type\": \"command\",\n            \"command\": \"osascript -e 'display notification \"$CLAUDE_NOTIFICATION\" with title \"Claude Code\"'\"\n          }\n        ]\n      }\n    ]\n  }\n}\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch3\u003eThông báo qua Slack webhook\u003c\/h3\u003e\n\u003cpre\u003e\u003ccode\u003e{\n  \"hooks\": {\n    \"Notification\": [\n      {\n        \"matcher\": \"\",\n        \"hooks\": [\n          {\n            \"type\": \"command\",\n            \"command\": \"curl -s -X POST -H 'Content-type: application\/json' --data '{\"text\":\"Claude Code: '\"$CLAUDE_NOTIFICATION\"'\"}' $SLACK_WEBHOOK_URL\"\n          }\n        ]\n      }\n    ]\n  }\n}\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch2\u003eBảo mật và các lưu ý quan trọng\u003c\/h2\u003e\n\u003cp\u003eHooks chạy lệnh shell với quyền của user hiện tại, nên cần lưu ý về bảo mật:\u003c\/p\u003e\n\n\u003ch3\u003eNguyên tắc bảo mật\u003c\/h3\u003e\n\u003cul\u003e\n  \u003cli\u003e\n\u003cstrong\u003eKhông hardcode secrets:\u003c\/strong\u003e Sử dụng biến môi trường cho API keys, webhook URLs. Không commit secrets vào settings.json\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003eValidate input:\u003c\/strong\u003e Biến $CLAUDE_TOOL_INPUT và $CLAUDE_FILE_PATH có thể chứa ký tự đặc biệt. Luôn đặt trong dấu ngoặc kép\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003eGiới hạn phạm vi:\u003c\/strong\u003e Sử dụng điều kiện (if\/case) để hook chỉ chạy trên file\/thư mục cần thiết, tránh ảnh hưởng không mong muốn\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003eKiểm tra exit code:\u003c\/strong\u003e PreToolUse hooks nên trả về exit code rõ ràng. Exit 0 = cho phép tiếp tục, exit khác 0 = chặn hành động\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003eTimeout:\u003c\/strong\u003e Hooks có giới hạn thời gian chạy. Nếu hook chạy quá lâu (ví dụ: full test suite trên project lớn), nó có thể bị kill\u003c\/li\u003e\n\u003c\/ul\u003e\n\n\u003ch3\u003eLưu ý về project-level settings\u003c\/h3\u003e\n\u003cpre\u003e\u003ccode\u003e\/\/ .claude\/settings.json (project-level)\n\/\/ File này được commit vào repo, mọi người trong team đều dùng\n\/\/ CHỈ đặt hooks phù hợp cho cả team\n\n{\n  \"hooks\": {\n    \"PostToolUse\": [\n      {\n        \"matcher\": \"Write\",\n        \"hooks\": [\n          {\n            \"type\": \"command\",\n            \"command\": \"npx prettier --write \"$CLAUDE_FILE_PATH\" 2\u0026gt;\/dev\/null || true\"\n          }\n        ]\n      }\n    ]\n  }\n}\n\n\/\/ Lưu ý: thêm \"|| true\" để hook không block nếu prettier\n\/\/ chưa được install trên máy đồng nghiệp\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch2\u003eDebugging hooks\u003c\/h2\u003e\n\u003cp\u003eKhi hook không hoạt động như mong đợi, có một số cách để debug:\u003c\/p\u003e\n\n\u003ch3\u003eGhi log để debug\u003c\/h3\u003e\n\u003cpre\u003e\u003ccode\u003e{\n  \"hooks\": {\n    \"PostToolUse\": [\n      {\n        \"matcher\": \"Write\",\n        \"hooks\": [\n          {\n            \"type\": \"command\",\n            \"command\": \"echo \"[$(date)] Tool: $CLAUDE_TOOL_NAME, File: $CLAUDE_FILE_PATH\" \u0026gt;\u0026gt; \/tmp\/claude-hooks.log \u0026amp;\u0026amp; npx prettier --write \"$CLAUDE_FILE_PATH\" 2\u0026gt;\u0026amp;1 \u0026gt;\u0026gt; \/tmp\/claude-hooks.log\"\n          }\n        ]\n      }\n    ]\n  }\n}\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003cp\u003eSau đó kiểm tra log:\u003c\/p\u003e\n\u003cpre\u003e\u003ccode\u003etail -f \/tmp\/claude-hooks.log\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch3\u003eCác lỗi thường gặp và cách xử lý\u003c\/h3\u003e\n\u003cul\u003e\n  \u003cli\u003e\n\u003cstrong\u003eHook không chạy:\u003c\/strong\u003e Kiểm tra matcher có đúng tên tool không. Tên tool phân biệt hoa thường — \"Write\" chứ không phải \"write\"\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003eCommand not found:\u003c\/strong\u003e Hook chạy trong shell mới, có thể không có PATH đầy đủ. Dùng đường dẫn tuyệt đối cho các lệnh hoặc đảm bảo PATH được thiết lập\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003eHook chạy nhưng không có hiệu lực:\u003c\/strong\u003e Kiểm tra xem file có nằm trong phạm vi điều kiện if\/case không. Thêm echo debug để xác nhận\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003ePreToolUse hook không chặn được hành động:\u003c\/strong\u003e Đảm bảo hook trả về exit code khác 0 khi muốn chặn. Dùng \"exit 1\" rõ ràng\u003c\/li\u003e\n\u003c\/ul\u003e\n\n\u003ch2\u003eKết hợp nhiều hooks\u003c\/h2\u003e\n\u003cp\u003eBạn có thể kết hợp nhiều hooks cho cùng một tool để tạo pipeline hoàn chỉnh:\u003c\/p\u003e\n\n\u003cpre\u003e\u003ccode\u003e{\n  \"hooks\": {\n    \"PostToolUse\": [\n      {\n        \"matcher\": \"Write\",\n        \"hooks\": [\n          {\n            \"type\": \"command\",\n            \"command\": \"case \"$CLAUDE_FILE_PATH\" in *.ts|*.tsx) npx prettier --write \"$CLAUDE_FILE_PATH\" ;; esac\"\n          },\n          {\n            \"type\": \"command\",\n            \"command\": \"case \"$CLAUDE_FILE_PATH\" in *.ts|*.tsx) npx eslint --fix \"$CLAUDE_FILE_PATH\" 2\u0026gt;\/dev\/null || true ;; esac\"\n          },\n          {\n            \"type\": \"command\",\n            \"command\": \"if echo \"$CLAUDE_FILE_PATH\" | grep -q 'src\/'; then bash .claude\/scripts\/smart-test.sh \"$CLAUDE_FILE_PATH\" 2\u0026gt;\/dev\/null || true; fi\"\n          }\n        ]\n      }\n    ]\n  }\n}\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003cp\u003eHooks trong mảng chạy tuần tự — Prettier trước, ESLint sau, cuối cùng là test. Nếu bất kỳ hook nào fail (trả về exit code khác 0), các hook sau vẫn tiếp tục chạy trừ khi bạn cấu hình khác.\u003c\/p\u003e\n\n\u003ch2\u003eCấu hình hooks thực tế cho các loại project\u003c\/h2\u003e\n\n\u003ch3\u003eProject React\/Next.js\u003c\/h3\u003e\n\u003cpre\u003e\u003ccode\u003e{\n  \"hooks\": {\n    \"PostToolUse\": [\n      {\n        \"matcher\": \"Write\",\n        \"hooks\": [\n          {\n            \"type\": \"command\",\n            \"command\": \"case \"$CLAUDE_FILE_PATH\" in *.ts|*.tsx|*.js|*.jsx|*.css) npx prettier --write \"$CLAUDE_FILE_PATH\" ;; esac\"\n          },\n          {\n            \"type\": \"command\",\n            \"command\": \"case \"$CLAUDE_FILE_PATH\" in *.ts|*.tsx) npx tsc --noEmit 2\u0026gt;\u0026amp;1 | head -20 ;; esac\"\n          }\n        ]\n      }\n    ],\n    \"PreToolUse\": [\n      {\n        \"matcher\": \"Bash\",\n        \"hooks\": [\n          {\n            \"type\": \"command\",\n            \"command\": \"if echo \"$CLAUDE_TOOL_INPUT\" | grep -q 'git commit'; then npx tsc --noEmit \u0026amp;\u0026amp; npx eslint src\/ --max-warnings 0; fi\"\n          }\n        ]\n      }\n    ]\n  }\n}\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch3\u003eProject Python\/Django\u003c\/h3\u003e\n\u003cpre\u003e\u003ccode\u003e{\n  \"hooks\": {\n    \"PostToolUse\": [\n      {\n        \"matcher\": \"Write\",\n        \"hooks\": [\n          {\n            \"type\": \"command\",\n            \"command\": \"case \"$CLAUDE_FILE_PATH\" in *.py) black \"$CLAUDE_FILE_PATH\" \u0026amp;\u0026amp; isort \"$CLAUDE_FILE_PATH\" \u0026amp;\u0026amp; ruff check \"$CLAUDE_FILE_PATH\" --fix ;; esac\"\n          }\n        ]\n      }\n    ]\n  }\n}\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch3\u003eProject Go\u003c\/h3\u003e\n\u003cpre\u003e\u003ccode\u003e{\n  \"hooks\": {\n    \"PostToolUse\": [\n      {\n        \"matcher\": \"Write\",\n        \"hooks\": [\n          {\n            \"type\": \"command\",\n            \"command\": \"case \"$CLAUDE_FILE_PATH\" in *.go) gofmt -w \"$CLAUDE_FILE_PATH\" \u0026amp;\u0026amp; go vet .\/... 2\u0026gt;\u0026amp;1 | head -10 ;; esac\"\n          }\n        ]\n      }\n    ]\n  }\n}\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch2\u003eHooks nâng cao: Conditional logic và custom scripts\u003c\/h2\u003e\n\u003cp\u003eKhi cần logic phức tạp hơn một dòng lệnh, tốt nhất là viết script riêng và gọi từ hook.\u003c\/p\u003e\n\n\u003ch3\u003eScript hook thông minh: Chỉ chạy test cho file thay đổi\u003c\/h3\u003e\n\u003cp\u003eTạo file .claude\/scripts\/post-write-hook.sh:\u003c\/p\u003e\n\u003cpre\u003e\u003ccode\u003e#!\/bin\/bash\n# post-write-hook.sh — Hook thông minh chạy sau khi Claude ghi file\nFILE=\"$CLAUDE_FILE_PATH\"\nPROJECT=\"$CLAUDE_PROJECT_DIR\"\n\n# 1. Auto-format dựa trên loại file\ncase \"$FILE\" in\n  *.ts|*.tsx|*.js|*.jsx)\n    npx prettier --write \"$FILE\" 2\u0026gt;\/dev\/null\n    ;;\n  *.py)\n    black \"$FILE\" 2\u0026gt;\/dev\/null\n    ;;\n  *.go)\n    gofmt -w \"$FILE\" 2\u0026gt;\/dev\/null\n    ;;\nesac\n\n# 2. Chạy test liên quan (nếu file nằm trong src\/)\nif echo \"$FILE\" | grep -q \"src\/\"; then\n  # Tìm file test tương ứng\n  TEST_FILE=$(echo \"$FILE\" | sed 's|src\/|test\/|' | sed 's|\\.ts$|.test.ts|')\n  if [ -f \"$TEST_FILE\" ]; then\n    echo \"[Hook] Running tests for $TEST_FILE\"\n    npx jest \"$TEST_FILE\" --bail --silent 2\u0026gt;\u0026amp;1 | tail -5\n  fi\nfi\n\n# 3. Log thay đổi\necho \"[$(date '+%H:%M:%S')] Written: $FILE\" \u0026gt;\u0026gt; \"$PROJECT\/.claude\/hooks.log\"\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003cp\u003eCấu hình hook gọi script:\u003c\/p\u003e\n\u003cpre\u003e\u003ccode\u003e{\n  \"hooks\": {\n    \"PostToolUse\": [\n      {\n        \"matcher\": \"Write\",\n        \"hooks\": [\n          {\n            \"type\": \"command\",\n            \"command\": \"bash .claude\/scripts\/post-write-hook.sh\"\n          }\n        ]\n      }\n    ]\n  }\n}\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch3\u003eHook bảo vệ file quan trọng\u003c\/h3\u003e\n\u003cp\u003eNgăn Claude vô tình ghi đè lên các file cấu hình quan trọng:\u003c\/p\u003e\n\u003cpre\u003e\u003ccode\u003e{\n  \"hooks\": {\n    \"PreToolUse\": [\n      {\n        \"matcher\": \"Write\",\n        \"hooks\": [\n          {\n            \"type\": \"command\",\n            \"command\": \"PROTECTED_FILES='.env .env.production docker-compose.yml package-lock.json yarn.lock'; for f in $PROTECTED_FILES; do if echo \"$CLAUDE_FILE_PATH\" | grep -q \"$f\"; then echo \"BLOCKED: $CLAUDE_FILE_PATH is a protected file.\" \u0026amp;\u0026amp; exit 1; fi; done\"\n          }\n        ]\n      }\n    ]\n  }\n}\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003cp\u003eKhi Claude cố ghi vào file được bảo vệ, hook sẽ trả về exit code 1 và thông báo lý do. Claude sẽ nhận được thông báo và thử cách tiếp cận khác hoặc hỏi bạn.\u003c\/p\u003e\n\n\u003ch2\u003eSo sánh hooks với các phương pháp tự động hóa khác\u003c\/h2\u003e\n\u003cp\u003eHooks không phải cách duy nhất để tự động hóa workflow trong Claude Code. Dưới đây là so sánh với các phương pháp khác:\u003c\/p\u003e\n\u003cul\u003e\n  \u003cli\u003e\n\u003cstrong\u003eCLAUDE.md instructions:\u003c\/strong\u003e Viết trong CLAUDE.md để nhắc Claude chạy lệnh. Ưu điểm: linh hoạt, dễ thay đổi. Nhược điểm: Claude có thể quên, không đảm bảo 100%\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003eGit hooks:\u003c\/strong\u003e Pre-commit, pre-push hooks truyền thống. Ưu điểm: hoạt động ngoài Claude Code. Nhược điểm: chỉ trigger khi commit\/push, không theo dõi file write\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003eClaude Code hooks:\u003c\/strong\u003e Trigger chính xác khi Claude dùng tool. Ưu điểm: đảm bảo 100%, đúng thời điểm. Nhược điểm: chỉ hoạt động trong Claude Code\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003eIDE extensions:\u003c\/strong\u003e Như format-on-save trong VS Code. Ưu điểm: quen thuộc. Nhược điểm: không áp dụng khi Claude Code chạy ngoài IDE\u003c\/li\u003e\n\u003c\/ul\u003e\n\u003cp\u003ePhương pháp tối ưu là kết hợp: Claude Code hooks cho auto-format và test nhanh, git hooks cho validation cuối cùng trước commit, và CLAUDE.md cho các hướng dẫn mềm.\u003c\/p\u003e\n\n\u003ch2\u003eHiệu suất và tối ưu hooks\u003c\/h2\u003e\n\u003cp\u003eHooks chạy đồng bộ — Claude Code phải đợi hook hoàn thành trước khi tiếp tục. Điều này có nghĩa là hooks chạy chậm sẽ làm giảm tốc độ làm việc đáng kể. Dưới đây là các mẹo tối ưu:\u003c\/p\u003e\n\u003cul\u003e\n  \u003cli\u003e\n\u003cstrong\u003eGiới hạn phạm vi file:\u003c\/strong\u003e Dùng điều kiện case\/if để hook chỉ chạy trên file cần thiết. Không chạy ESLint cho file JSON, không chạy Prettier cho file Python\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003eDùng --bail cho test:\u003c\/strong\u003e Flag --bail dừng test suite ngay khi có test đầu tiên fail, tiết kiệm thời gian chờ\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003eChuyển hướng output:\u003c\/strong\u003e Dùng 2\u0026gt;\/dev\/null và | tail -N để giảm output không cần thiết. Claude Code đọc output của hook, output dài làm chậm xử lý\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003eTránh install trong hook:\u003c\/strong\u003e Không chạy npm install hoặc pip install trong hook. Đảm bảo dependencies đã được install trước khi làm việc\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003eDùng cache:\u003c\/strong\u003e Nhiều linter hỗ trợ cache (ESLint --cache, mypy --incremental). Kích hoạt cache giúp lần chạy thứ hai nhanh hơn nhiều\u003c\/li\u003e\n\u003c\/ul\u003e\n\n\u003ch3\u003eĐo thời gian chạy hook\u003c\/h3\u003e\n\u003cpre\u003e\u003ccode\u003e{\n  \"hooks\": {\n    \"PostToolUse\": [\n      {\n        \"matcher\": \"Write\",\n        \"hooks\": [\n          {\n            \"type\": \"command\",\n            \"command\": \"START=$(date +%s%N); npx prettier --write \"$CLAUDE_FILE_PATH\" 2\u0026gt;\/dev\/null; END=$(date +%s%N); echo \"Hook took $(( (END-START)\/1000000 ))ms\" \u0026gt;\u0026gt; \/tmp\/claude-hooks-timing.log\"\n          }\n        ]\n      }\n    ]\n  }\n}\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003cp\u003eKiểm tra log timing để xác định hook nào chạy chậm và cần tối ưu. Mục tiêu là mỗi hook hoàn thành trong dưới 2 giây — nếu lâu hơn, người dùng sẽ cảm thấy Claude Code phản hồi chậm.\u003c\/p\u003e\n\n\u003ch2\u003eBước tiếp theo\u003c\/h2\u003e\n\u003cp\u003eHooks là công cụ mạnh mẽ biến Claude Code thành một phần không thể tách rời trong quy trình phát triển. Bắt đầu đơn giản — thêm một PostToolUse hook cho auto-format — rồi mở rộng dần sang lint, test, và notification khi bạn đã quen với cơ chế hoạt động. Quan trọng nhất là hooks phải giúp bạn làm việc nhanh hơn chứ không chậm hơn — nếu hook chạy quá lâu hoặc hay gây lỗi giả, hãy điều chỉnh hoặc gỡ bỏ. Tìm hiểu thêm các tính năng nâng cao tại \u003ca href=\"\/collections\/nang-cao\"\u003eThư viện Nâng cao Claude\u003c\/a\u003e.\u003c\/p\u003e\n","brand":"Minh Tuấn","offers":[{"title":"Default Title","offer_id":47730150801620,"sku":null,"price":0.0,"currency_code":"VND","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0821\/0264\/9044\/files\/claude-code-hooks-t_-d_ng-hoa-workflow-v_i-pre-post-hooks.jpg?v=1774715543","url":"https:\/\/claude.vn\/products\/claude-code-hooks-t%e1%bb%b1-d%e1%bb%99ng-hoa-workflow-v%e1%bb%9bi-pre-post-hooks","provider":"CLAUDE.VN","version":"1.0","type":"link"}