| name | commit-draft |
| description | 未コミットのdiffを分析し、Claude Code内で「!」付きで即実行できるgit commit(+push)コマンドを生成する |
| user-invocable | true |
| disable-model-invocation | true |
| allowed-tools | ["Bash(git -C * status*)","Bash(git -C * diff*)","Bash(git -C * log*)","Bash(git -C * rev-parse*)","Bash(git rev-parse*)","Bash(ls *)","Bash(head*)","Bash(grep*)","Read(/tmp/commit-draft*)","Read(**/lefthook.yml)","Read(**/.husky/*)","Write(/tmp/commit-draft*)"] |
commit-draft — diffからコミットコマンドを生成
未コミットの変更を分析し、Claude Codeのプロンプトに ! 付きで貼り付けて即実行できるコミットコマンドを出力する。
手順
1. 状態とスタイルの取得(1回のBashにまとめる)
差分の中身(フルdiff)はこの段階ではまだ読まない。 まず --stat(ファイル一覧+増減行数)までを1回のBashコマンドでまとめて取得する。これがトークン節約と高速化の要 — 往復が減り、巨大ファイルの中身をコンテキストに流し込まずに済む。cd は使わない(Bashツールの不要な権限プロンプトを避けるため)。
ROOT=$(git rev-parse --show-toplevel) && \
git -C "$ROOT" status && \
git -C "$ROOT" diff --stat && \
git -C "$ROOT" diff --cached --stat && \
git -C "$ROOT" log --oneline -5 && \
ls "$ROOT"/lefthook.yml "$ROOT"/.husky/prepare-commit-msg 2>/dev/null
ROOT の値(リポジトリルートの絶対パス)は、出力するシェルスクリプトにも絶対パスとしてハードコードする(後述)。スクリプト内では git rev-parse を実行時に呼ばない
git status に -uall フラグは使わない
- 末尾の
ls で prepare-commit-msg hookの有無だけを安く確認する。ヒットした場合のみ、必要に応じて該当ファイルをReadして内容(プレフィックス自動付与の有無)を確かめる。存在しなければそれ以上読まない
2. 差分内容の取得(必要なものだけ)
--stat の結果を見て、コミットメッセージ作成に内容の理解が必要なファイルだけフル差分を取る。全ファイルのフル差分を無条件に取得しない。
内容を読まずstatだけで判断するもの(フルdiffをスキップ):
*.jsonl などの会話ログ・生成記録、*.lock 等のロックファイル、ビルド生成物、node_modules、巨大バイナリ
--stat で数百行を超える大規模差分のファイル
これらはファイル名と増減行数から目的が自明(例: 「会話ログ追加」「依存更新」)なため、中身を読む価値がない。
内容を読むもの: 上記以外の、ソース・ドキュメント・設定の実質的な変更。
フル差分は除外pathspecで取得し、保険でバイト上限を付ける(除外パターンはリポジトリの生成物に応じて調整する):
git -C "$ROOT" diff --cached -- . ':(exclude)*.jsonl' ':(exclude)*.lock' | head -c 20000
git -C "$ROOT" diff -- . ':(exclude)*.jsonl' ':(exclude)*.lock' | head -c 20000
head -c で頭打ちになった(出力が途中で切れた)場合は、残りはstatベースで判断し、メッセージは要約レベルに留める。差分が全てスキップ対象(例: jsonlのみのコミット)なら、フルdiffコマンド自体を省略してstatだけでメッセージを決める。
秘密情報の軽量チェック
フル差分を取得したついでに、staged差分に対し秘密情報の混入を安くスキャンする:
git -C "$ROOT" diff --cached | grep -inE 'api[_-]?key|secret|password|passwd|token|BEGIN [A-Z ]*PRIVATE KEY' | head -20
ヒットがあれば commit-draft の出力時にユーザーへ警告する(後述)。ヒットが多数・判断に迷う・コード変更を含む重要コミットの場合に限って、commit-reviewer サブエージェントを起動して第三者レビューを依頼する。日常的なノート・ログ更新では起動しない(毎回サブエージェントを焚くとトークンと時間の無駄になるため)。
3. 変更の分析
差分(必要な範囲)の内容から以下を判断する:
- 変更の目的(バグ修正、機能追加、リファクタ、設定変更など)
- 論理的なコミット単位への分割が必要か
- コミットメッセージの行数(後述の判断基準を参照)
- Claudeが変更に関与したか(後述のCo-Authored-By判定を参照)
コミットメッセージの行数判断
1行目(要約行)は常に必要。本文(3行目以降)を付けるかどうかは変更の性質で判断する。
1行で十分なケース:
- 変更の意図が要約行だけで伝わる(リネーム、typo修正、依存更新、単純な追加・削除など)
- 例:
未使用のisTargetJobUrl関数を削除
複数行にすべきケース:
- 「なぜ」この変更をしたかが要約行だけでは伝わらない
- 複数ファイルにまたがる変更で、変更箇所の関係性を説明したい
- 既存の動作を変えるため、変更前後の違いを示したい
- 代替案があった中で特定のアプローチを選んだ理由がある
迷ったら1行でよい。ただし、「要約行を読んだレビュアーが『なぜ?』と思うかどうか」を基準にする。
Co-Authored-By の判定
このセッション内でClaudeが変更に関与した場合、コミットメッセージの末尾に Co-Authored-By トレーラーを追加する。
関与ありと判断するケース:
- Claudeがコード変更を提案し、ユーザーがそれを採用した
- ClaudeがEdit/Writeツールで直接ファイルを編集した
- Claudeが生成したコードをユーザーが貼り付けて適用した
- Claudeとユーザーの共同作業で変更が生まれた
関与なしと判断するケース:
- ユーザーが自分で書いた変更に対し、コミットコマンドの生成のみを依頼された
- Claudeはコードレビューやアドバイスのみで、変更内容自体はユーザーが独自に作成した
- このセッション内でClaudeがEdit/Writeツールを使った形跡がなく、ユーザーからも「Claudeが書いた」という明示がない場合
デフォルトはCo-Authored-By なし。確実にClaudeが変更に関与したと判断できる場合のみ付ける。「このセッションで自分がコードを書いた/編集した記憶があるか?」を基準にする。コミットコマンドの生成だけを頼まれた場合は関与なし。
フォーマット:
トレーラーはコミットメッセージ本文の最後に空行を挟んで配置する。モデル名はセッションで使用中のモデルを記載する(例: Claude Opus 4.8 (1M context), Claude Sonnet 4.6)。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4. コミット単位の決定
1コミットにまとめる場合
複数コミットに分割する場合
- 異なる目的の変更が混在している場合
- 分割する場合は実行順序を明示する
5. 出力
出力方式
Claude Codeの ! プレフィックスは内部で (eval) を使うため、heredocや複数行の入力は動作しない。また、長い1行コマンドを直接表示すると、ターミナル幅で折り返し+インデントが入りコピペが壊れる。
この問題を回避するため、コマンドをシェルスクリプトに書き出し、短い実行コマンドを提示する方式を取る。
手順:
- まずReadツールで
/tmp/commit-draft.sh を読み込む(存在しなくてもエラーが返るだけでよい。Writeツールは事前にReadが必要なため)。その後Writeツールでシェルスクリプトを書き出す
- スクリプトの中身をコードブロックでユーザーに表示する(何が実行されるか確認できるようにするため)
- 実行用の短いコマンドを提示する:
! bash /tmp/commit-draft.sh
スクリプトの書き方
- 先頭で必ずgitリポジトリのルートに
cd する — 手順1で取得した絶対パスをハードコードする(git rev-parse を実行時に呼ばない)。スクリプトがどのディレクトリから実行されても、常に正しいリポジトリで動作するようにするため
git add . や git add -A は使わない — 必ずファイル名を個別指定する
set -e を付ける — エラー時に即停止するため
1行メッセージの例:
#!/bin/bash
set -e
cd "/absolute/path/to/repo-root"
git add file1 file2
git commit -m "要約行"
複数行メッセージの例(heredoc):
#!/bin/bash
set -e
cd "/absolute/path/to/repo-root"
git add file1 file2
git commit -F - <<'EOF'
要約行
本文: なぜこの変更をしたか、補足事項など
EOF
Co-Authored-By付きの例:
#!/bin/bash
set -e
cd "/absolute/path/to/repo-root"
git add file1 file2
git commit -F - <<'EOF'
要約行
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
EOF
複数行 + Co-Authored-By付きの例:
#!/bin/bash
set -e
cd "/absolute/path/to/repo-root"
git add file1 file2
git commit -F - <<'EOF'
要約行
本文: なぜこの変更をしたか、補足事項など
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
EOF
シェルスクリプト内ではheredoc(<<'EOF')が使えるため、git commit -F - でstdinからメッセージを読み取る形式にする。-m 連結より見やすく、改行や空行の制御も自然にできる。1行メッセージの場合のみ -m を使う。
push の同梱
コミット後の !git push 手打ちを不要にするため、リモート(origin)が存在するリポジトリでは、スクリプト末尾に push を既定で含める。
- 現在ブランチに upstream が未設定なら
git push -u origin <branch>、設定済みなら git push
- 複数コミットに分割する場合は最後のスクリプトにのみ push を付ける(途中で失敗したら push されないように)
- 含めないケース: ユーザーが push 不要と明示した場合/リモートが無い場合/detached HEAD
- 手順1のコマンドに
git -C "$ROOT" remote と git -C "$ROOT" rev-parse --abbrev-ref @{upstream} の確認を足すと1往復で判定できる(upstream 未設定はエラーになるだけでよい)
- スクリプト表示時に「push まで実行します」と一言添える
prepare-commit-msg hookへの対応
lefthook.yml や .husky/prepare-commit-msg 等の設定を確認し、prepare-commit-msg hookがコミットメッセージにプレフィックス(例: feat(TICKET-123): )を自動付与するか調べる。自動付与される場合、スクリプト内のコミットメッセージにはプレフィックスを含めず、本文のみを記述する。
例えば、ブランチ名 feat/TICKET-456_add_feature から hookが feat(TICKET-456): を自動付与する場合:
- 正しい:
git commit -m "ユーザー一覧にフィルタ機能を追加"
- 誤り:
git commit -m "fix(TICKET-456): ユーザー一覧にフィルタ機能を追加" → feat(TICKET-456): fix(TICKET-456): ... と重複する
その他のルール
- コミットメッセージは直近のコミットのスタイルに合わせる(日本語/英語、prefixの有無など)
- 複数コミットの場合は番号付きで順序を明示する
- コマンド以外の説明は最小限にする — 何をコミットするかの1行説明のみ
- .env、credentials等の機密ファイルが含まれている場合は警告する
複数コミットの場合
複数コミットに分割する場合は、各コミットを別々のスクリプトファイルとして書き出す(/tmp/commit-draft-1.sh, /tmp/commit-draft-2.sh, ...)。実行コマンドもそれぞれ提示し、1つずつ順番に実行できるようにする。
6. 変更がない場合
git status でコミット対象がなければ「未コミットの変更はありません」とだけ伝える。