Pemeriksaan Kode Otomatis dengan Hooks: Kualitas Terjamin Saat Menulis

Gunakan PostToolUse Hooks untuk memicu lint otomatis setelah Claude menulis file. Exit code 2 mengirim error kembali ke Claude untuk perbaikan otomatis.


Claude Code menulis kode dengan cepat, tapi tidak selalu ingat menjalankan lint. Anda minta dia "sekalian periksa setelah menulis", kadang dilakukan, kadang terlewat. Hooks menyelesaikan masalah ini — memindahkan tanggung jawab pemeriksaan kode dari Claude ke sistem.

Artikel ini fokus pada satu hal: menggunakan PostToolUse Hooks untuk memicu pemeriksaan otomatis setelah Claude menulis kode, sehingga setiap perubahan file memenuhi standar kode Anda.


Pendekatan: PostToolUse + Filter Tipe File

Logika Hook pemeriksaan kode sederhana:

  1. Claude memanggil Write atau Edit untuk menulis file
  2. Hook menangkap event ini dan mendapatkan path file
  3. Berdasarkan ekstensi, menentukan tool pemeriksaan yang akan dijalankan
  4. Pemeriksaan lolos: exit code 0, Claude lanjut
  5. Pemeriksaan gagal: exit code 2, output error dikirim ke Claude untuk diperbaiki

Exit code 2 adalah kuncinya — membuat Claude menerima error dan memperbaiki otomatis, membentuk siklus "tulis→periksa→perbaiki".


Konfigurasi Dasar: Proyek Satu Bahasa

Ruby (RuboCop)

{
  "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'"
          }
        ]
      }
    ]
  }
}

JavaScript/TypeScript (ESLint)

{
  "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'"
          }
        ]
      }
    ]
  }
}

Python (ruff)

{
  "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'"
          }
        ]
      }
    ]
  }
}

Konfigurasi Lanjutan: Proyek Multi-Bahasa

Pindahkan logika pemeriksaan ke script terpisah agar lebih mudah dikelola.

Buat .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

Referensikan script di settings.json:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [{ "type": "command", "command": "bash .claude/hooks/lint.sh" }]
      }
    ]
  }
}

Membuat Claude Memperbaiki Otomatis

Exit code 2 + stderr adalah kunci perbaikan otomatis 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
}

Mengecualikan File yang Tidak Perlu Diperiksa

skip_patterns=("vendor/" "node_modules/" "db/schema.rb" ".min.js" "_test.go")
for pattern in "${skip_patterns[@]}"; do
  [[ "$file" == *"$pattern"* ]] && exit 0
done

Hasil Nyata

Setelah dikonfigurasi, alur kerja menjadi:

  1. Anda minta Claude menulis file Ruby
  2. Claude menulis dengan tool Write
  3. Hook otomatis menjalankan rubocop -A — menemukan 3 masalah
  4. RuboCop memperbaiki 2 otomatis; yang ke-3 butuh penilaian manusia
  5. Error ke-3 dikirim ke Claude via stderr
  6. Claude memperbaiki kode, Hook terpicu lagi
  7. Pemeriksaan kedua lolos — selesai

Anda tidak perlu menjalankan perintah pemeriksaan secara manual.


Global vs. Level Proyek

Global (~/.claude/settings.json): Cocok saat menggunakan aturan yang sama di semua proyek.

Level proyek (.claude/settings.json di-commit ke git): Direkomendasikan untuk tim. Siapa pun yang membuka repositori langsung mendapat konfigurasi yang benar.

Keduanya bisa berdampingan dan digabungkan saat dijalankan.


Ringkasan

Pemeriksaan kode otomatis dengan Hooks bermuara pada tiga langkah:

  1. PostToolUse + matcher "Write|Edit" untuk menangkap penulisan file
  2. Dapatkan path file dan arahkan ke linter yang sesuai berdasarkan ekstensi
  3. Saat gagal, keluar dengan exit code 2 agar Claude melihat error dan memperbaikinya

Mulai dari satu bahasa. Setelah berjalan, tambahkan yang lain. Commit script ke .claude/hooks/ untuk dibagikan ke tim.