はじめに
Claude Code を始めに、よくAI Agent に任せるタスクとしてコンフリクト対応があります。
しかし、ほんのたまーにコンフリクト対応とは別の不要な対応をすることがありました。
タイポの修正や調査中に不具合を発見してくれたならまだ良いのですが、Pull Request のスコープ範囲外のものを追加して欲しくはありません。
そこで、自分なりにハーネスを作成して、コンフリクト対応を依頼したらコンフリクト対応だけ行うようにさせました。
やった対応
以下は「はじめに」で語った課題感について、具体的にどのような対応をしたかを解説します。
方向性
コンフリクト解消だけに絞り込む場合、プロンプトでスコープを明示するだけでは不十分です。
そのため、hook で強制する二段構えをするのが確実です。
なぜかというと、プロンプトは「お願い」レベルのものなので、AI Agent が「ついでに直しておきました」を防ぎきれません。
hook は実行前にブロックできる保証レベルの機能になります。
よって、以下の機能を作成します。
- skills
- hook
スキルの作成
今回はAIが実装中にコンフリクトが発生したら自分で対応するように、Skills として作成します。
新しい skills を作成するには、.claude/skills/resolve-conflicts/SKILL.md を作成します。
そして、SKILL.md に以下の内容を記述していきます。
---
allowed-tools: Bash(git diff:*), Bash(git status:*), Bash(git add:*), Read, Edit
description: マージ/リベースのコンフリクトのみを解消する
---
## 現在コンフリクト中のファイル
!`git diff --name-only --diff-filter=U`
## 指示
上記のファイルに含まれる `<<<<<<<` / `=======` / `>>>>>>>` の
コンフリクトマーカーの解消のみを行うこと。
**やって良いこと:**
- コンフリクトブロック内でどちらの変更を採用するか判断する
- 両方の変更を意味的に統合する(必要な場合のみ)
**やってはいけないこと:**
- コンフリクトと無関係なコードのリファクタリング・整形
- コンフリクトと無関係なバグ修正や機能追加
- テストの追加・実行(指示がない限り)
- git commit / git push(指示がない限り)
**必須の報告:**
作業完了後、以下を必ず報告すること:
1. 解消したファイル一覧と、それぞれどちらの変更を採用したか
2. 判断に迷った箇所
3. コンフリクト解消中に気づいた「本来直したいが今回は触っていない」問題点
(これは別タスクとして提案するだけで、コードは変更しないこと)
Hook の作成
上記でも説明したとおり、プロンプトだけだと、「ついでにここも修正しておきました!」が発生する可能性があるので、PreToolUse hook でコンフリクト中のファイル以外への Edit / Write を機械的にブロックします。
PreToolUse とは、Claude Code の Hook 機能で定義されている Event の一つで、ツールが呼び出される前に実行される Event です。
今回は、ファイルを Edit / Write するタイミングで Hook を発火して、AI が対応してもよいかどうかを機械的に判定させます。
Claude Code ではこれ以外にも複数の Hook Event があります。詳細は以下の Hooks ドキュメントを参照してください。
.claude/hooks/conflict-scope-guard.sh ファイルを作成して、shell script で以下のように作成します。
#!/bin/bash
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null) || exit 0
[ -z "$REPO_ROOT" ] && exit 0
CONFLICTED=$(git -C "$REPO_ROOT" diff --name-only --diff-filter=U -z | tr '\0' '\n')
# コンフリクトが無い = 通常作業 → ガード無効
[ -z "$CONFLICTED" ] && exit 0
REL_PATH="${FILE_PATH#"$REPO_ROOT"/}"
if ! printf '%s\n' "$CONFLICTED" | grep -qxF "$REL_PATH"; then
echo "Blocked: '$REL_PATH' はコンフリクト中のファイルではありません。" >&2
exit 2
fi
exit 0
作成したら、.claude/settings.json に hook の設定をします。
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write|MultiEdit",
"hooks": [
{ "type": "command", "command": "bash \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/conflict-scope-guard.sh" }
]
}
]
}
}
これで、コンフリクトになっていないファイルへの書き込みは exit code 2 で機械的に拒否され、claude 側にエラーメッセージとして返されます。
運用してみて
現状、上記の設定で問題なく対応を進めることができています。
hook は shellscript なので、例えば
今回のマージで変更されたファイル全体まで確認させた
と思ったら
git diff --name-only <merge-base>
に変えても問題ないと思います。
この辺りは個人やチームのお好みです。