Hệ thống Hook
Module 11.3: Hệ thống Hook
Phần tiêu đề “Module 11.3: Hệ thống Hook”Thời gian học: ~30 phút
Yêu cầu trước: Module 11.2 (SDK Integration)
Kết quả: Sau module này, bạn sẽ hiểu hook concept, biết common pattern, và implement custom hook cho workflow của mình.
1. WHY — Tại sao cần Hook?
Phần tiêu đề “1. WHY — Tại sao cần Hook?”Claude Code write file → bạn muốn intercept thêm logic: lint check, audit log, Slack notify, block sensitive files. Hook cho phép tự động intercept code tại moment cần thiết. Ví von: Pre-hook = kiểm tra trước vào. Post-hook = ghi log sau. Không hook = phải manual post-process.
2. CONCEPT — Khái niệm Hook System
Phần tiêu đề “2. CONCEPT — Khái niệm Hook System”Hook là custom code chạy tại specific event trong Claude Code lifecycle. Giống Git hooks, middleware, hoặc lifecycle methods.
Hook Types
Phần tiêu đề “Hook Types”| Hook Type | Thời điểm chạy | Có block được? | Use Case |
|---|---|---|---|
| Pre-hook | Trước action | ✅ Exit 1 = block | Validation, lint, backup |
| Post-hook | Sau action | ❌ Không undo | Logging, notification, cleanup |
Hook Events ⚠️ Cần xác minh
Phần tiêu đề “Hook Events ⚠️ Cần xác minh”Các event có thể có trong hook system:
| Event | Khi nào trigger | Typical Use Case |
|---|---|---|
pre-file-write | Trước khi write file | Lint check, format validation |
post-file-write | Sau khi write file | Audit log, git add, notify |
pre-command | Trước execute command | Security check, approval gate |
post-command | Sau command xong | Log result, metrics |
post-session | Session end | Summary report, cleanup, Slack |
Hook Script Interface
Phần tiêu đề “Hook Script Interface”Hook nhận context via env vars: CLAUDE_EVENT, CLAUDE_FILE_PATH, CLAUDE_SESSION_ID, CLAUDE_USER. Exit code: 0=allow, 1=block (pre-hook only).
Hook Flow
Phần tiêu đề “Hook Flow”graph TB A[Claude Code action] --> B{Pre-hook exists?} B -->|Yes| C[Execute pre-hook] B -->|No| E[Execute action] C --> D{Exit code?} D -->|0| E D -->|1| F[Block action - show error] E --> G{Post-hook exists?} G -->|Yes| H[Execute post-hook] G -->|No| I[Done] H --> IHook Configuration
Phần tiêu đề “Hook Configuration”Hook config trong JSON file hooks.json: { "hooks": { "event": "./path/script.sh" } }
3. DEMO — Setup Hook Workflow
Phần tiêu đề “3. DEMO — Setup Hook Workflow”Scenario: Team setup hook cho workflow gồm lint check, audit log, và Slack notification.
Step 1-2: Setup & Logging hook
Phần tiêu đề “Step 1-2: Setup & Logging hook”mkdir -p ~/claude-projects/my-app/.claude/hookscd ~/claude-projects/my-app/.claude/hooksTạo audit-log.sh — log mọi file change:
cat > audit-log.sh << 'EOF'#!/bin/bash# Post-hook: Audit log for file changes
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")LOG_FILE=".claude/audit.log"
# Create JSON log entryecho "{ \"timestamp\": \"$TIMESTAMP\", \"event\": \"$CLAUDE_EVENT\", \"file\": \"$CLAUDE_FILE_PATH\", \"session\": \"$CLAUDE_SESSION_ID\", \"user\": \"$CLAUDE_USER\"}" >> "$LOG_FILE"
echo "✓ Logged change to $LOG_FILE"exit 0EOF
chmod +x audit-log.shStep 3: Lint check hook (pre-file-write)
Phần tiêu đề “Step 3: Lint check hook (pre-file-write)”cat > lint-check.sh << 'EOF'#!/bin/bashFILE="$CLAUDE_FILE_PATH"if [[ "$FILE" =~ \.(ts|js)$ ]]; then npx eslint "$FILE" --quiet && exit 0 echo "❌ Lint check failed - blocking write" exit 1fiexit 0EOFchmod +x lint-check.shStep 4: Notification hook (post-session)
Phần tiêu đề “Step 4: Notification hook (post-session)”Tạo notify-slack.sh — gửi summary khi session end:
cat > notify-slack.sh << 'EOF'#!/bin/bash# Post-hook: Slack notificationAUDIT_COUNT=$(wc -l < .claude/audit.log 2>/dev/null || echo 0)curl -X POST "$SLACK_WEBHOOK_URL" \ -H 'Content-Type: application/json' \ -d "{\"text\": \"Claude session done. Files: $AUDIT_COUNT\"}" \ --silentexit 0EOFchmod +x notify-slack.shExpected output:
# File created and made executableStep 5: Configure & Test
Phần tiêu đề “Step 5: Configure & Test”Tạo hooks.json:
cat > ../hooks.json << 'EOF'{ "hooks": { "pre-file-write": ".claude/hooks/lint-check.sh", "post-file-write": ".claude/hooks/audit-log.sh", "post-session": ".claude/hooks/notify-slack.sh" }}EOFTest the hooks:
Write file qua Claude Code & verify hooks trigger:
claude -p "Add error handling to src/utils.ts"# Expected: lint check runs, then audit log created, Slack sent
cat .claude/audit.log # Verify log entries exist4. PRACTICE — Tự làm
Phần tiêu đề “4. PRACTICE — Tự làm”Exercise 1: Audit Log Hook
Phần tiêu đề “Exercise 1: Audit Log Hook”Goal: Tạo post-file-write hook log change với format CSV cho Excel import
Instructions:
- Tạo
audit-csv.shtrong.claude/hooks/ - Format:
timestamp,file,user - Append vào
.claude/audit.csv - Test bằng cách cho Claude write file
Expected result: File .claude/audit.csv có entry mới mỗi lần write
💡 Hint
Dùng date, echo với >>, CSV format đơn giản.
✅ Solution
#!/bin/bashTIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")CSV_FILE=".claude/audit.csv"
# Create header if file doesn't existif [[ ! -f "$CSV_FILE" ]]; then echo "timestamp,file,user" > "$CSV_FILE"fi
# Append entryecho "$TIMESTAMP,$CLAUDE_FILE_PATH,$CLAUDE_USER" >> "$CSV_FILE"
echo "✓ Logged to CSV"exit 0Đừng quên chmod +x và add vào hooks.json.
Exercise 2: Protection Hook
Phần tiêu đề “Exercise 2: Protection Hook”Goal: Tạo pre-file-write hook block write vào sensitive files
Instructions:
- Tạo
protect-sensitive.sh - Block write vào
.env,*.key,config/production.json - Show error message khi block
- Test bằng cách try write vào
.env
Expected result: Write vào .env bị block với clear error message
💡 Hint
Pattern matching [[ "$FILE" =~ pattern ]], exit 1 để block.
✅ Solution
#!/bin/bashFILE="$CLAUDE_FILE_PATH"
# Check sensitive patternsif [[ "$FILE" == ".env" ]] || \ [[ "$FILE" =~ \.key$ ]] || \ [[ "$FILE" == "config/production.json" ]]; then
echo "❌ BLOCKED: Cannot modify sensitive file" echo "File: $FILE" echo "Manual review required for production changes" exit 1fi
# Allow all other filesexit 0Add vào hooks.json:
{ "hooks": { "pre-file-write": ".claude/hooks/protect-sensitive.sh" }}Exercise 3: Notification Pipeline
Phần tiêu đề “Exercise 3: Notification Pipeline”Goal: Tạo post-session hook gửi summary qua Discord webhook
Instructions:
- Tạo
notify-discord.sh - Count files changed từ audit log
- Send Discord webhook với embed format
- Test sau khi end session
Expected result: Discord message với summary sau session
💡 Hint
Discord dùng embeds array khác Slack text format.
✅ Solution
#!/bin/bashAUDIT_COUNT=$(wc -l < .claude/audit.log 2>/dev/null || echo 0)PAYLOAD="{\"content\": \"Claude session done - Files: $AUDIT_COUNT, User: $CLAUDE_USER\"}"curl -X POST "$DISCORD_WEBHOOK_URL" \ -H 'Content-Type: application/json' \ -d "$PAYLOAD" \ --silentexit 0Set environment variable:
export DISCORD_WEBHOOK_URL="https://discord.com/api/webhooks/..."5. CHEAT SHEET
Phần tiêu đề “5. CHEAT SHEET”Hook Types
Phần tiêu đề “Hook Types”| Type | Timing | Can Block? | Common Use |
|---|---|---|---|
| Pre-hook | Before action | ✅ Yes (exit 1) | Validation, lint, approval |
| Post-hook | After action | ❌ No | Logging, notification, metrics |
Hook Script Template
Phần tiêu đề “Hook Script Template”#!/bin/bash# Hook: [event-name]
# Available env vars:# - CLAUDE_EVENT# - CLAUDE_FILE_PATH (file hooks)# - CLAUDE_SESSION_ID# - CLAUDE_USER
# Your logic here
# Pre-hook exit codes:exit 0 # Allow actionexit 1 # Block action
# Post-hook:exit 0 # SuccessHook Configuration ⚠️ Cần xác minh
Phần tiêu đề “Hook Configuration ⚠️ Cần xác minh”{ "hooks": { "event-name": "path/to/script.sh" }, "hookTimeout": 30000}Config location: .claude/hooks.json (⚠️ Cần xác minh)
Quick Commands
Phần tiêu đề “Quick Commands”# Make hook executablechmod +x .claude/hooks/my-hook.sh
# Test hook manuallyCLAUDE_EVENT=post-file-write \CLAUDE_FILE_PATH=test.ts \.claude/hooks/my-hook.sh
# Check hook exit codeecho $? # 0 = success, 1 = blocked/failed
# Debug hookbash -x .claude/hooks/my-hook.sh # Show execution trace6. PITFALLS — Lỗi thường gặp
Phần tiêu đề “6. PITFALLS — Lỗi thường gặp”| ❌ Mistake | ✅ Correct Approach |
|---|---|
Quên chmod +x | Always chmod +x sau tạo script |
| Dùng exit 1 trong post-hook | Post-hook chạy sau — không undo. Dùng pre-hook block. |
| Hook chạy slow | Keep <500ms, dùng async cho slow ops |
| Hardcode path | Dùng env variables, portable |
| Hook fail silent | Check exit code, add debug echo |
| Không test standalone | Test manual với mock env vars trước |
7. REAL CASE — Production Audit Trail
Phần tiêu đề “7. REAL CASE — Production Audit Trail”Scenario: Fintech company Việt Nam cần audit trail cho mọi code change bởi AI. Compliance regulation mandate log WHO changed WHAT WHEN và WHY.
Problem:
- Dev dùng Claude Code viết code
- Compliance team cần audit log export mỗi tháng
- Manual logging không practical — dev quên hoặc bỏ qua
Solution: Hook-based audit system
Implementation:
#!/bin/bash# .claude/hooks/audit-log.sh — Key parts onlyTIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")LOG_ENTRY="{\"timestamp\": \"$TIMESTAMP\", \"file\": \"$CLAUDE_FILE_PATH\", \"user\": \"$CLAUDE_USER\"}"echo "$LOG_ENTRY" >> /var/log/claude-audit/audit.jsonl# Async send to central service(curl -X POST https://logs.company.internal/audit -d "$LOG_ENTRY" --silent &) &exit 0Hook config:
{ "hooks": { "post-file-write": ".claude/hooks/audit-log.sh", "post-command": ".claude/hooks/audit-log.sh" }}Result: 100% audit coverage, zero manual logging, compliance export automated, dev workflow unaffected.
Tiếp theo: Module 11.4: GitHub Actions Integration →