Dùng PostToolUse Hooks để tự động kích hoạt lint sau khi Claude viết file. Exit code 2 gửi lỗi lại cho Claude tự động sửa.
Claude Code viết code nhanh, nhưng không phải lúc nào cũng nhớ chạy lint. Bạn bảo nó "vừa viết vừa kiểm tra luôn", đôi khi làm, đôi khi bỏ qua. Hooks giải quyết vấn đề này — chuyển trách nhiệm kiểm tra code từ Claude sang hệ thống.
Bài viết này tập trung vào một điều: dùng PostToolUse Hooks để tự động kích hoạt kiểm tra sau khi Claude viết code, đảm bảo mọi thay đổi file đều đáp ứng tiêu chuẩn của bạn.
Logic của Hook kiểm tra code rất đơn giản:
Write hoặc Edit để viết fileExit code 2 là chìa khóa — khiến Claude nhận lỗi và tự động sửa, tạo vòng lặp "viết→kiểm tra→sửa".
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "bash -c 'file=$(echo \"$CLAUDE_TOOL_INPUT\" | jq -r .file_path 2>/dev/null); [[ \"$file\" == *.rb ]] && rubocop -A \"$file\" --no-color -q || true'"
}
]
}
]
}
}
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "bash -c 'file=$(echo \"$CLAUDE_TOOL_INPUT\" | jq -r .file_path 2>/dev/null); [[ \"$file\" =~ \\.(js|ts|jsx|tsx)$ ]] && npx eslint --fix \"$file\" || true'"
}
]
}
]
}
}
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "bash -c 'file=$(echo \"$CLAUDE_TOOL_INPUT\" | jq -r .file_path 2>/dev/null); [[ \"$file\" == *.py ]] && ruff check --fix \"$file\" && ruff format \"$file\" || true'"
}
]
}
]
}
}
Chuyển logic kiểm tra vào script độc lập để dễ bảo trì hơn.
Tạo .claude/hooks/lint.sh:
#!/bin/bash
set -e
input=$(cat)
file=$(echo "$input" | jq -r '.tool_input.file_path // empty')
[[ -z "$file" ]] && exit 0
[[ ! -f "$file" ]] && exit 0
ext="${file##*.}"
case "$ext" in
rb) rubocop -A "$file" --no-color -q ;;
js|ts|jsx|tsx) npx eslint --fix "$file" --quiet ;;
py) ruff check --fix "$file"; ruff format "$file" ;;
go) gofmt -w "$file"; golangci-lint run "$file" 2>&1 ;;
*) exit 0 ;;
esac
Tham chiếu script trong settings.json:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [{ "type": "command", "command": "bash .claude/hooks/lint.sh" }]
}
]
}
}
Exit code 2 + stderr là chìa khóa kích hoạt tự động sửa của Claude:
run_lint() {
local output exit_code
output=$(rubocop "$file" --no-color 2>&1)
exit_code=$?
if [[ $exit_code -ne 0 ]]; then
echo "$output" >&2
exit 2
fi
rubocop -A "$file" --no-color -q
}
skip_patterns=("vendor/" "node_modules/" "db/schema.rb" ".min.js" "_test.go")
for pattern in "${skip_patterns[@]}"; do
[[ "$file" == *"$pattern"* ]] && exit 0
done
Sau khi cấu hình, quy trình làm việc trở thành:
Writerubocop -A — phát hiện 3 vấn đềBạn không cần chạy bất kỳ lệnh kiểm tra thủ công nào.
Global (~/.claude/settings.json): Phù hợp khi dùng cùng quy tắc cho tất cả dự án.
Cấp độ dự án (.claude/settings.json commit vào git): Khuyến nghị cho team. Ai mở repo cũng tự động có cấu hình đúng.
Hai cấp độ có thể cùng tồn tại và được hợp nhất khi chạy.
Kiểm tra code tự động với Hooks chỉ gồm ba bước:
PostToolUse + matcher "Write|Edit" để chặn ghi fileBắt đầu với một ngôn ngữ. Khi hoạt động, thêm ngôn ngữ khác. Commit script vào .claude/hooks/ để chia sẻ với team.