Claude Media
Claude Codeのコーディング規約をレビュー指摘から作る — rulesへの書き分け

Claude Codeのコーディング規約をレビュー指摘から作る — rulesへの書き分け

PRレビューのコメントをgh apiで集め、claude -pで頻出指摘に分類し、CLAUDE.mdと.claude/rulesのpaths付きファイルへ書き分ける手順です。置き場所の判断表と読み込みの確認方法も載せます。

Claude Codeのコーディング規約をレビュー指摘から作る — rulesへの書き分け

レビューで同じ指摘を何度も受けているなら、その指摘はClaude Codeの規約として書き起こす価値があります。公式のメモリ解説も、CLAUDE.mdに足すタイミングとして「コードレビューでClaudeが知っているべきだった点を指摘されたとき」を挙げています。

ここでやるのは、過去のPRレビューコメントをまとめて取得し、頻出する指摘を分類して、置き場所ごとに規約ファイルへ書き分けるところまでです。流れは4段階です。

手順

レビュー指摘から規約ファイルまでの流れ

  1. 1

    コメントを集める

    gh apiでPRレビューコメントを取得し、人間のコメントだけを残します。

  2. 2

    頻出指摘に分類する

    claude -pに渡し、指摘の型ごとの件数と規約案をJSONで返させます。

  3. 3

    置き場所を決める

    CLAUDE.md、paths付きrules、hooksのどれに置くかを指摘ごとに振り分けます。

  4. 4

    読み込みを確かめる

    InstructionsLoadedフックで、規約が意図したタイミングで読まれたかを見ます。

レビューコメントを集めて下ごしらえする

取得に使うのは、リポジトリ全体のレビューコメントを返すエンドポイントGET /repos/{owner}/{repo}/pulls/commentsです。クエリにはsort、direction、since、per_pageがあり、1ページは最大100件(既定30件)です。レスポンスの各要素には、指摘対象のファイルを示すpath、差分の断片diff_hunk、コメント本文body、投稿者userが入っています。

gh apiは--paginateで全ページを順に取得します。取得結果はそのままClaudeに渡さず、jqで必要な項目だけに絞ります。

gh api "repos/OWNER/REPO/pulls/comments?per_page=100&since=2026-01-01T00:00:00Z" \
  --paginate \
  | jq -c '.[]
      | select(.user.type == "User")
      | {path, body, hunk: (.diff_hunk | .[0:400])}' \
  > review-comments.jsonl
 
wc -l -c review-comments.jsonl

絞り込みには理由が2つあります。

  • select(.user.type == "User")で、レスポンスのuser.typeがボットのコメントを外します。自動レビューの定型文が頻出指摘の上位に居座るのを避けるためです
  • diff_hunkを先頭400文字に切り、ファイルが大きいPRでも1行が膨らまないようにします

最後のwcは、次の工程の入力サイズを見るためです。claude -pに標準入力で渡せる量は10MBまでで、超えるとエラー終了します。数千件のコメントがあるリポジトリでは、sinceで期間を区切るかpathの接頭辞で分けて、複数回に分けます。上限に当たった場合の絞り方はclaude -pにログを渡すと10MB上限で止まるにまとめています。

claude -pで頻出指摘をJSONに分類する

分類はclaude -pの構造化出力で行います。--output-format jsonと--json-schemaを組み合わせると、スキーマに沿った結果が応答のstructured_outputフィールドに入ります。非対話実行の基本はclaude -pオプションとはで扱っています。

ここでは、指摘ごとに「型の名前」「件数」「規約の文面案」「対象パスのglob」「置き場所の候補」を返させます。

cat > schema.json <<'EOF'
{
  "type": "object",
  "properties": {
    "themes": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "name": {"type": "string"},
          "count": {"type": "integer"},
          "rule_text": {"type": "string"},
          "path_glob": {"type": "string"},
          "destination": {"enum": ["claude-md", "rules", "hook", "skill"]}
        },
        "required": ["name", "count", "rule_text", "path_glob", "destination"]
      }
    }
  },
  "required": ["themes"]
}
EOF
 
cat review-comments.jsonl | claude --bare -p \
  "各行はPRレビューコメント(path, body, hunk)です。繰り返し出る指摘を型ごとにまとめ、
各型について件数、検証可能な1文の規約案、対象のglob、置き場所を返してください。
1件しか出ない指摘は含めないでください。" \
  --output-format json \
  --json-schema "$(cat schema.json)" \
  | jq '.structured_output.themes | sort_by(-.count)'

--bareを付ける点に意味があります。公式ドキュメントによると、--bareはCLAUDE.mdや自動メモリ、フック、MCPサーバーの自動読み込みを飛ばします。分類の最中に、既存のCLAUDE.mdの内容が結果へ混ざらず、手元と同じ結果をほかのマシンでも再現できます。--bareはスクリプトやSDK呼び出しで推奨されるモードとされています。

構造化出力を使うときは、structured_outputが存在するか確かめてから後続の処理に進めます。Claudeが依頼を断った場合など、有効な構造化出力なしで終わった実行では、このフィールドが応答に含まれません。上のパイプではjqがnullを返して気づけます。JSONにtotal_cost_usdも入るので、コメントの件数を増やす前に1回あたりの費用を見ておけます。

置き場所は「いつ読ませたいか」で決める

返ってきたdestinationはあくまで案です。最終的な振り分けは、規約を読ませたい頻度で決めます。メモリ解説の記述に沿うと、次の表になります。

指摘の性質置き場所根拠
どのファイルを触るときも守る事実(ビルド・テストコマンド、命名、ディレクトリ構成)置き場所CLAUDE.md根拠毎セッションの開始時に読まれる
特定のディレクトリや拡張子にだけ効く規約置き場所paths付きの.claude/rules/*.md根拠一致するファイルを読んだときだけ読み込まれる
コミット前など決まった時点で必ず実行したいこと置き場所hooks根拠フックはライフサイクルの決まった時点でシェルコマンドとして実行される
複数手順からなる作業の型置き場所skill根拠呼び出し時か関連すると判断されたときだけ読まれる

CLAUDE.mdは強制される設定ではなく、文脈として渡されます。公式の説明では、命令が具体的で検証可能なほど守られやすくなります。「適切にエラー処理する」ではなく「APIハンドラーはAppErrorを投げ、catchで握りつぶさない」のように、レビュアーが差分を見て可否を言える粒度にします。

CLAUDE.mdは1ファイル200行以内が目安で、長いと文脈を消費して遵守率が下がるとされています。分類結果をすべてCLAUDE.mdに足すと、この目安をすぐ超えます。警告の見方はCLAUDE.md is too long警告の意味と直し方で確認できます。件数の多い指摘のうち全域に効くものだけをCLAUDE.mdに残し、残りはrulesへ回します。

paths付きrulesファイルに書き分ける

.claude/rules/には、1トピック1ファイルでMarkdownを置きます。サブディレクトリに分けても再帰的に読み込まれます。先頭のYAMLフロントマターにpathsを書くと、そのglobに一致するファイルをClaudeが読んだときにだけ文脈へ入ります。pathsを持たないルールは、CLAUDE.mdと同じ優先度で起動時に読み込まれます。

分類結果が「APIハンドラーでのエラー処理」と「Reactコンポーネントのprops」だったとして、書き分けた例を示します。以下は、頻出指摘が出たと仮定した例示で、規約の中身はリポジトリごとに置き換えてください。

mkdir -p .claude/rules
 
cat > .claude/rules/api-errors.md <<'EOF'
---
paths:
  - "src/api/**/*.ts"
---
 
# APIハンドラーのエラー処理
 
- 例外は`AppError`を継承したクラスで投げる。`throw new Error()`は使わない
- `catch`節で握りつぶさない。ログに出すか再スローする
- 入力検証は`validateBody()`を通す。ハンドラー内で手書きしない
EOF
 
cat > .claude/rules/react-props.md <<'EOF'
---
paths:
  - "src/components/**/*.tsx"
---
 
# コンポーネントのprops
 
- propsの型は`interface`で宣言し、ファイル先頭に置く
- boolean propsの名前は`is`か`has`で始める
EOF

ルールのフロントマターで読まれるフィールドはpathsだけです。ほかのフィールドを書いてもエラーにならず、無視されます。descriptionのような項目を足しても効かないので、説明は本文の見出しに書きます。pathsは複数パターンを並べるか、src/**/*.{ts,tsx}のようにブレース展開でまとめられます。YAMLが解釈できないときは、フロントマターごと無視されて、pathsなしのルールとして起動時に読み込まれます。claude --debugで解析エラーを確認できます。

新しい規約は1つの指摘につき1行から始める

分類結果のrule_textは、そのまま貼らずにレビュアーの目で読み直します。レビューコメントは「この場合はこうしてほしい」という文脈つきの指摘なので、一般化しすぎると別の場面で誤って適用されます。直し方の目安は次のとおりです。

  • 例外が付く指摘は、例外条件まで規約に書く
  • 好みの問題で意見が割れていた指摘は、規約にしない
  • リンターやフォーマッターで検出できる指摘は、規約ではなくそれらの設定に移す
  • コミットメッセージの規約は、別ファイルに切り出して扱う。Claude Codeとcommitlintに手順があります

規約が意図どおり読まれているかを確かめる

規約を足したのにClaudeが従わないとき、原因が文面なのか読み込みなのかを切り分けます。起動時に読まれるファイルは、セッション内で/contextを実行し、Memory filesの一覧で確認します。サブディレクトリのCLAUDE.mdやpaths付きrulesは、起動時ではなく該当ファイルを読んだときに読み込まれるため、この一覧には出ません。

読み込みのタイミングを記録するには、InstructionsLoadedフックを使います。CLAUDE.mdや.claude/rules/*.mdが文脈に入るたびに発火し、読み込みの理由(session_start、path_glob_matchなど)とファイルのパスを受け取ります。ブロックや内容の変更はできず、観測用です。

cat > .claude/settings.local.json <<'EOF'
{
  "hooks": {
    "InstructionsLoaded": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "jq -c '{reason: .load_reason, file: .file_path, trigger: .trigger_file_path}' >> /tmp/instructions-loaded.log"
          }
        ]
      }
    ]
  }
}
EOF

この設定でセッションを開き、src/api/配下のファイルを読ませると、ログにpath_glob_matchの行が追記されます。paths付きルールが読まれるのは、ClaudeがRead・Write・Editでファイルを扱ったとき、またはcatやheadで単一ファイルを読むBashコマンドを実行したときです。Writeによる新規ファイル作成で読まれない既知の挙動はClaude Code rulesがWriteの新規ファイルで読まれない問題で扱っています。

ログに行が出ないときの切り分けは、次の3点です。

  1. pathsのglobが対象ファイルに一致しているか。パターンは、ルールを置いた.claudeフォルダを含むディレクトリからの相対で解釈されます
  2. 共有用に.claude/rules/へシンボリックリンクを置いていないか。リンク先がワーキングディレクトリの外だと、外部インポートの承認が済むまで読み込まれず、承認後もpaths付きのものは読み込まれません。詳しくは.claude/rulesのシンボリックリンクが読み込まれない原因と対処を参照してください
  3. 規約ファイルが大きすぎないか。オンデマンドで読み込まれるファイルには文字数の上限があり、200Kのコンテキストウィンドウでは40,000文字です。上限を超えると先頭部分だけがClaudeに渡されます。確認方法はCLAUDE.mdが途中までしか読まれない上限と確認法にあります

規約を保守し続けるために

規約は足すだけだと矛盾が増えます。CLAUDE.mdとrulesで同じ事柄に違う指示があると、Claudeはどちらかを任意に選ぶ可能性があると説明されています。ユーザー階層の~/.claude/rules/とプロジェクトのルールが衝突した場合も同様です。

古い規約の掃除には、/doctor prompt-auditが使えます。古いモデル向けの記述、存在しないファイルやコマンドへの言及、ファイル同士の矛盾を探して提案を出し、適用は依頼するまで行いません。実行にはv2.1.283以降が必要です。CLAUDE.mdの監査と更新をプラグインで行う方法はclaude-md-managementプラグインで紹介しています。

チームで運用する場合は、取り込む指摘の基準をあらかじめ決めておくと混乱が減ります。レビュー体制と規約の置き場所を合わせて設計する考え方は、Claude Codeチーム導入ガイドにあります。

ClaudeによるPRレビューを使っている場合は、同じ種類の指摘が繰り返し出るときに、レビュー用の指示ファイルREVIEW.mdへルールを足す選択肢もあります。REVIEW.mdは、PR自身の変更ではなくベースブランチかデフォルトブランチのものが読まれます。コード生成側の規約はCLAUDE.mdとrules、レビュー側の基準はREVIEW.mdと、役割で分けて考えます。

まとめ

レビュー指摘の規約化は、取得、分類、置き場所の振り分け、読み込みの確認という4つの手順で進められます。分類まではclaude -pに任せられますが、規約にするかどうかの判断は人の仕事です。最初は件数の多い指摘3〜5件に絞り、paths付きrulesに1ファイルずつ書いて、InstructionsLoadedのログで読まれることを確かめてから広げると、規約の数が膨らんでも運用が崩れません。

参照した一次ソース

この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。

  1. code.claude.com/docs/en/memory
  2. code.claude.com/docs/en/headless
  3. code.claude.com/docs/en/hooks
  4. docs.github.com/en/rest/pulls/comments
  5. cli.github.com/manual/gh_api
この記事を共有:XはてブLinkedIn