Skip to content

コミット前に自動でlint+テストを走らせる

Claudeがコードを編集するたびに、自動的にlintとテストを実行します。問題があればClaudeに通知し、自動修正を促します。「Claudeがきれいに書いてくれたと思ったら、lintエラーがたくさんあった」という状況を防ぎます。

Hooks: Claude Codeの特定の操作(ツール実行前後など)に独自のシェルコマンドを自動実行できる設定機能。

基本パターン(ファイル保存後にlintを実行)

Section titled “基本パターン(ファイル保存後にlintを実行)”
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "FILE=$(echo $CLAUDE_TOOL_INPUT | jq -r '.file_path'); if [[ \"$FILE\" == *.ts || \"$FILE\" == *.tsx || \"$FILE\" == *.js ]]; then cd $CLAUDE_PROJECT_DIR && npx eslint --fix \"$FILE\" 2>&1; fi"
}
]
}
]
}
}

テスト実行パターン(テストファイルに変更があれば自動実行)

Section titled “テスト実行パターン(テストファイルに変更があれば自動実行)”
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "FILE=$(echo $CLAUDE_TOOL_INPUT | jq -r '.file_path'); if [[ \"$FILE\" == *test* || \"$FILE\" == *spec* ]]; then cd $CLAUDE_PROJECT_DIR && npx vitest run \"$FILE\" 2>&1 | tail -20; fi"
}
]
}
]
}
}

コミット前に全テストを実行するHook(PreToolUseでブロック)

Section titled “コミット前に全テストを実行するHook(PreToolUseでブロック)”
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "COMMAND=$(echo $CLAUDE_TOOL_INPUT | jq -r '.command'); if echo \"$COMMAND\" | grep -q 'git commit'; then cd $CLAUDE_PROJECT_DIR && npm run test -- --run 2>&1; fi"
}
]
}
]
}
}
コミット: Gitでファイルの変更を履歴として記録する操作。npm: Node.jsのパッケージ管理ツール。`npm install` でライブラリをインストールする。

複雑なロジックはシェルスクリプトにまとめると管理しやすくなります。

~/.claude/hooks/lint-on-save.sh:

#!/bin/bash
set -e
# 対象ファイルを取得
FILE_PATH=$(echo "$CLAUDE_TOOL_INPUT" | jq -r '.file_path')
# TypeScript/JavaScript ファイルのみ対象
if [[ ! "$FILE_PATH" =~ \.(ts|tsx|js|jsx)$ ]]; then
exit 0
fi
# プロジェクトディレクトリに移動
cd "$CLAUDE_PROJECT_DIR"
echo "🔍 Linting: $FILE_PATH"
# ESLint を実行(自動修正あり)
if ! npx eslint --fix "$FILE_PATH" 2>&1; then
echo "❌ ESLintエラーが残っています。確認してください。"
exit 0 # エラーでも続行(ブロックしない)
fi
# TypeScriptの型チェック(TypeScriptファイルのみ)
if [[ "$FILE_PATH" =~ \.tsx?$ ]]; then
if ! npx tsc --noEmit --skipLibCheck 2>&1 | head -20; then
echo "⚠️ TypeScriptの型エラーがあります"
fi
fi
echo "✅ Lint完了"
Terminal window
chmod +x ~/.claude/hooks/lint-on-save.sh

settings.json から呼び出す:

{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "~/.claude/hooks/lint-on-save.sh"
}
]
}
]
}
}
TypeScript: JavaScriptに型定義を追加した言語。型チェックによりバグを事前に発見しやすくなる。
  1. ClaudeがEdit/Writeツールでファイルを保存する
  2. PostToolUse Hookが発火
  3. lint-on-save.sh が実行される
  4. TypeScript/JSファイルの場合のみlintが実行される
  5. ESLintが自動修正できるものは修正
  6. 修正できないエラーがある場合は、次のClaude応答に「ESLintエラーがあります」と通知
  7. Claudeが通知を受け取り、エラーを修正する
#!/bin/bash
FILE_PATH=$(echo "$CLAUDE_TOOL_INPUT" | jq -r '.file_path')
if [[ ! "$FILE_PATH" =~ \.rb$ ]]; then
exit 0
fi
cd "$CLAUDE_PROJECT_DIR"
bundle exec rubocop --autocorrect "$FILE_PATH" 2>&1

Pythonプロジェクト向け(black + flake8)

Section titled “Pythonプロジェクト向け(black + flake8)”
#!/bin/bash
FILE_PATH=$(echo "$CLAUDE_TOOL_INPUT" | jq -r '.file_path')
if [[ ! "$FILE_PATH" =~ \.py$ ]]; then
exit 0
fi
cd "$CLAUDE_PROJECT_DIR"
black "$FILE_PATH" 2>&1
flake8 "$FILE_PATH" 2>&1

Goプロジェクト向け(gofmt + go vet)

Section titled “Goプロジェクト向け(gofmt + go vet)”
#!/bin/bash
FILE_PATH=$(echo "$CLAUDE_TOOL_INPUT" | jq -r '.file_path')
if [[ ! "$FILE_PATH" =~ \.go$ ]]; then
exit 0
fi
gofmt -w "$FILE_PATH" 2>&1
cd "$CLAUDE_PROJECT_DIR"
go vet ./... 2>&1
  • eslint --fix は自動修正しますが、修正後の結果をClaudeは認識しません。修正後の内容がClaudeの想定と異なる場合があります。
  • テストの実行時間が長いプロジェクトでは、全テストではなく変更ファイルに関連するテストだけ実行するように調整してください。
  • PostToolUse Hookは非ブロッキング(終了コードに関わらず続行)として動作します。エラーはClaudeへの通知として扱われます。