👍
Claude Code Hooks を使ってコードレビューや品質向上をシフトレフトする
モチベーション
ふたつある。
自分で書く場合の効率向上
軸を明文化しやすい部分は code agent にレビューしてもらえるようになったが、基本的に Pull Request 単位での依頼となる。
常々 PR ベースでのコードレビューはベストではないと考えており、書いたそばからレビューされないかなあ、と考えていた。
- コード書くときが一番そのコードをわかってる
- ちょっとでも時間経つと思い出すのにコストがかかる
 
 - レビューでの指摘によっては作業しなおし
- もっと早く見てほしかった
 
 - 手元に引っ張ってこないと LSP などコード理解のためのツールを使えない
- PR 画面だけでレビューするのはつらみ
 
 
要はコードレビューは QA 活動としても機能するのでシフトレフトするとうまみがあるよね、という発想。
また、組織のルールが CI に集約されるが、これも別に一般的な CI システムでなく手元で書くそばからルールに準拠しているかがわかると良いよね、というのもコミだ。 formatting や linting など定型のものだけでなく、自然言語でないと指示が難しいものもルールとして扱えるようにしたい。
生成コードの品質向上
生成したコードは次の点で品質としては問題になりやすい。
- コピペコード
 - ベタ書きコード
 - すぐルール忘れる問題
 
これらは生成されたコードに関する適切な評価関数がないために生じている問題でもある。生成〜評価までを自動化したい、というニーズ。
どうするか?
Claude Code の Stop Hooks を使う。
スクリプトは次の通り。
.claude/maintainability.sh
#!/bin/sh
# https://docs.anthropic.com/ja/docs/claude-code/hooks#stop%E3%81%A8subagentstop%E5%85%A5%E5%8A%9B
# 無限ループを避けるため stop_hook_active が true なら何もしない
INPUT=$(timeout 1 cat 2>/dev/null || echo "")
STOP_HOOK_ACTIVE=$(echo "$INPUT" | jq -r '.stop_hook_active // false')
if [ "$STOP_HOOK_ACTIVE" = "true" ]; then
  exit 0
fi
# git repo でない場合、何もしない
CURRENT_BRANCH=$(git branch --show-current 2>/dev/null || echo "unknown")
if [ "$CURRENT_BRANCH" = "unknown" ]; then
  exit 0
fi
# commit 済み main との差分
COMMITTED_DIFF=$(git diff --name-only main..HEAD 2>/dev/null)
# staging 済み
STAGED_FILES=$(git diff --name-only --cached 2>/dev/null)
# staging 前の変更
UNSTAGED_FILES=$(git diff --name-only 2>/dev/null)
CHANGED_FILES=$(printf '%s\n%s\n%s' "${COMMITTED_DIFF}" "${STAGED_FILES}" "${UNSTAGED_FILES}" | sort -u | grep -v '^$')
# 変更がない場合、何もしない
if [ -z "$CHANGED_FILES" ]; then
  exit 0
fi
REASON_TEXT="対象ファイル
${CHANGED_FILES}
次のドキュメントに記載されている観点をチェックしてください
- @docs/quality-gate.md
- @docs/dev-guideline.md"
if [ -z "$INPUT" ]; then
  # 手動実行時
  echo "$REASON_TEXT"
  exit 0
else
  # Claude Hooks 経由
  # https://docs.anthropic.com/ja/docs/claude-code/hooks#stop%2Fsubagentstop%E6%B1%BA%E5%AE%9A%E5%88%B6%E5%BE%A1
  cat << EOF >&2
{
  "decision": "block",
  "reason": "$REASON_TEXT"
}
EOF
  exit 2
fi
docs/quality-gate.md には品質チェック用のプロンプトを書く。
docs/quality-gate.md
# Quality Gate
- 関数の行数は 50 行まで
- DRY 原則の遵守
  - 10 行以上の重複は関数化
  - 全く同じ関数の重複定義は行数に関わらず共通化
- YAGNI 原則の遵守
  - 未使用コードは削除
- React コンポーネント以外の関数はテスト必須
- 使っていないファイルがないこと
docs/dev-guideline.md には開発時のガイドラインを書いておこう。
docs/dev-guideline.md
## コードスタイル
- `interface`ではなく `type` で型定義する
- React コンポーネントの props 名は`Props`
- if 節は長さにかかわらず必ず `{}` でくくる
- コメントは not why に絞って書く
  - 日本語常体で書く
## レビュー
コード変更後は変更内容の説明とコード全体を含めて次の観点で簡易レビューを実施
- セキュリティ
- 可読性
- パフォーマンス
実際使う場合はここを自分好みにする。一応これくらい簡易なものでも割と効く。
観点を別ファイルに切り出しているので、適宜ここだけ変えるのも CLAUDE.md から参照させるのもやりやすい。
で、次のように設定する。
.claude/settings.json
{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "/path/to/your/project/.claude/maintainability.sh"
          }
        ]
      }
    ]
  }
}
生成されたコードはこれで即座にチェック対象となる。自分で書いたものをレビューしてほしい場合は適当に会話を投げると見てくれる。
> あ
⏺ はい、何かお手伝いできることがありますか?
> Stop hook feedback:
  - [~/work/being-ish/carnelian/.claude/maintainability.sh]: {
    "decision": "block",
    "reason": "対象ファイル
  .claude/maintainability.sh
  次のドキュメントに記載されている観点をチェックしてください
  - @docs/quality-gate.md
  - @docs/dev-guideline.md"
  }
Discussion