---
title: "Claude Codeのコミットメッセージ生成が遅いのでツール呼び出しをゼロにした"
description: "Claude Codeスキルの動的コンテキスト注入でgit diffを事前に本文へ埋め込み、サブエージェントの推論パスを3回から1回に減らした手順を紹介します。"
category: "AI"
tags: ["Claude Code","Conventional Commits"]
publishedAt: "2026-06-11"
lastmod: "2026-08-18"
---

Claude Codeのスキル本文に `` !`git diff --cached` `` と書くと、スキル起動時にコマンドが先に実行され、その出力が本文へ埋め込まれます。コミットメッセージ生成スキルをこの方式へ作り直し、サブエージェントのツール呼び出しをなくしました。

````markdown
## Staged diff

```!
git diff --cached
```
````

この記事では、遅さの原因だったツール往復をどう特定し、どう設計し直したかを紹介します。

## コミットメッセージ生成は軽い処理のはずだった

ラビーでは、Claude Codeのスキルやエージェント定義を [LabeeHive/standards](https://github.com/LabeeHive/standards) というリポジトリで一元管理しています。その中の commit-message スキルは、ステージ済みの変更から Conventional Commits 形式のメッセージを生成するもので、コミット前に `/commit-message` と打つだけで提案が返ってきます。

今年の2月に作った最初の版は、こういう構成でした。

```yaml
---
name: commit-message
description: Generate commit messages following conventional commits.
model: haiku
context: fork
agent: general-purpose
allowed-tools: Bash(git:*)
---
```

モデルは最小の haiku、`context: fork` でメイン会話から切り離した使い捨てコンテキストで実行する。軽量にしたつもりでした。本文では生成手順をこう指示していました。

```markdown
## Process

1. Run `git diff --cached --stat` to see staged files
2. Run `git diff --cached` to see actual changes
3. Analyze the changes
4. Generate appropriate commit message
```

ところが実際に使うと、メッセージ1行が返るまでに体感で数十秒待たされます。haiku を選んでいるのに、です。

## 遅さの原因はモデルではなくツール往復

フォークされたサブエージェントの動きを追うと、原因はモデルの速度ではありませんでした。上の手順だと、エージェントは1回目の推論で `git diff --cached --stat` を発行し、結果を受け取って2回目の推論で `git diff --cached` を発行し、その結果を読んでから3回目の推論でようやくメッセージを書きます。つまり1行の出力のために推論パスが最低 **3回** 走ります。各往復にはツール実行の許可判定やプロセス起動も挟まるため、モデルを速くしても待ち時間は縮みません。

![エージェントに自分でgitコマンドを実行させると、推論①でgit diff --statを発行し、推論②でgit diffを発行し、推論③でようやくメッセージを書く。動的コンテキスト注入で起動前にdiffを本文へ埋め込むと、推論①でそのままメッセージが出て往復が消える](/images/posts/claude-code-skill-context-injection/round-trips.svg)

もうひとつ、`agent: general-purpose` は起動時に CLAUDE.md を読み込みます。コミットメッセージの生成にプロジェクトの開発指示は不要なので、これも無駄なコンテキストでした。

## 動的コンテキスト注入で diff を先に渡す

Claude Code には [dynamic context injection](https://code.claude.com/docs/en/skills) という仕組みがあります。スキル本文に `` !`コマンド` `` と書くと、本文が Claude に送られる前にコマンドが実行され、出力がその場所を置き換えます。Claude がツールとして実行するのではなく、ハーネス側の前処理です。Claude が受け取る時点では、結果が埋め込まれた完成形のプロンプトになっています。

この仕組みを使って、エージェントに取得させていたデータをすべて事前注入に置き換えました。

````markdown
## Staged files

!`git diff --cached --stat`

## Staged diff

```!
git diff --cached
```

## Recent commits (style reference)

!`git log --oneline -15`
````

1行のコマンドは `` !`...` `` のインライン形式、複数行や出力が長くなるものは ```! で開くフェンスブロックを使います。エージェントが起動した時点で diff もスタイル参照用の直近コミットも本文に揃っているため、指示は「データはすべて注入済み。コマンドを実行しないこと」だけで済みます。`allowed-tools: Bash(git:*)` も外しました。ツールを持たないエージェントは、許可判定そのものが発生しません。

ついでにスタイル参照として `git log --oneline -15` を注入したことで、リポジトリごとの scope の使い方など、既存コミットの慣習に寄せた提案が返るようになりました。旧版にはなかった入力です。

## エージェントを Explore に切り替える

`context: fork` で実行するエージェント種別も general-purpose から Explore に変えました。公式ドキュメントによると、組み込みの Explore と Plan はコンテキストを小さく保つために CLAUDE.md と git status の読み込みをスキップします。フォークされたスキルが受け取るのは、SKILL.md 本文とエージェント自身のシステムプロンプトだけになります。

データは注入済みでツールも使わないのですから、読み取り専用の Explore で十分です。general-purpose を選んだままだと、生成のたびに CLAUDE.md のトークンを読み込む分だけ入力が膨らみます。

## 出力をコードブロック1つに固定する

`context: fork` ではサブエージェントの最終応答が、そのまま結果としてメイン側に返ります。応答に「この変更は〜なので」のような解説文が混ざると、提案されるコミットメッセージにその文章まで含まれてしまいます。

そこで出力の形を固定しました。応答はフェンスコードブロック1個のみ、前後に文章を置かない、diff が空のときはブロックの中身を `No staged changes.` にする、という指定です。送信前のセルフチェックとして「コードブロックは1個か」「subject は50文字以内か」「type は判定表の最初に一致した規則か」の3項目を本文末尾に置き、生成のばらつきを抑えています。

作り直した結果、1回の生成にかかる推論はツール往復なしの1パスだけになりました。haiku の応答速度がそのまま体感になります。

## 運用するうえでの制限

この方式に切り替えるときに把握しておくべき制限がいくつかあります。

- 注入は元ファイルに対して1回だけ走ります。コマンド出力に `` !`...` `` の形が含まれていても再展開はされません
- インライン形式は `!` が行頭か空白の直後にあるときだけ認識されます。`KEY=` の直後のように別の文字に続く位置では文字列のまま残ります
- `settings.json` で `"disableSkillShellExecution": true` が設定されている環境では、コマンドは **実行されず** 置換メッセージに差し替えられます。managed settings で配布されている組織では事前に確認が必要です
- 注入コマンドはスキル起動のたびに走るため、重いコマンドを書くと起動そのものが遅くなります。diff が巨大なリポジトリでは、埋め込まれる本文もそのぶん膨らみます

スキル定義の現物は LabeeHive/standards に置いてあります。

::card[https://github.com/LabeeHive/standards]

## 追記 この記事の frontmatter はもう残っていません

上に載せた frontmatter のうち、いま残っているのは `name` と `description` だけです。`model` `context: fork` `agent` は、ここには載せていない `effort` と合わせて7月に全スキルから外し、`allowed-tools` も8月に外しました。

外した理由は `context: fork` にあります。fork で走ったスキルは利用者に届かないので、承認を待つ段があるとそこで黙って止まります。残りはその fork を設定するための項目だったので、まとめて落としました。分離が要るスキルは、`context: fork` ではなく Agent ツールでサブエージェントを立てる形に変えています。

`model` については、そのあと別の事情も分かりました。2.1.227 以降、対話セッションでは記録されるだけで適用されません（`claude -p` では効きます）。削除したのが先で、効かないと分かったのは後です。

::card[/posts/workflow-depends-on-behavior]

注入そのものは今も使っていて、対象を参照ファイルにまで広げました。

::card[/posts/skill-references-injected-not-read]