🔧

Claude CodeとContext Engineering。カスタムコマンドを追加してコンテキスト付与を自動化

に公開

@dyoshikawaです。

Claude Codeにカスタムスラッシュコマンドを追加してみたら良い感じだったので紹介します。

コンテキストエンジニアリングの重要性

AIコーディングに取り組んでいると、いかにしてAIに的確なコンテキストを与えられるか、そしてそれを自動化できるかのゲームであると感じています。最近は「コンテキストエンジニアリング」という言葉も登場しているようです。

https://cognition.ai/blog/dont-build-multi-agents

2025年において、世の中のモデルは極めて高い知能を持っています。しかし、最も賢い人間であっても、何を求められているかという文脈なしには効果的に仕事をすることはできません。「プロンプトエンジニアリング」は、LLMチャットボットにとって理想的な形式でタスクを記述するための取り組みを表す用語として生まれました。「コンテキストエンジニアリング」は、これの次のレベルです。動的なシステムにおいて、これを自動的に行うことです。より繊細さが求められ、AIエージェントを構築するエンジニアにとって事実上最も重要な仕事となっています。

/clear した後に良い感じにここまでの作業のコンテキストを与えたい

Claude Codeとやりとりしながら作業していると自然にコンテキストが積み上がるわけですが、コンテキストが一定長を超えるとLLMの性能は低下します。「カタログスペック上のコンテキストウィンドウ」と「実用上のコンテキストウィンドウ」は全く別です。

この現象は「pit of death」と呼ばれたりします。これについては次の記事で紹介しています。

https://zenn.dev/dyoshikawa/articles/developers-still-need-to-understand-ais-code

pit of deathが生じ始めたらコンテキストをリセットします。Claude Codeの場合は /clear スラッシュコマンドを叩くわけですが、今度はその後のプロンプトを的確に効かせるために「ここまで何をしたか」の概要は与えたくなります。

カスタムスラッシュコマンドで解決する

都度「ここまでこういうことやってたんだけど、続きとして〇〇をして」というように「ここまでのあらすじ」を説明して指示するのは手間なので、カスタムコマンドを作成することで自動化しました。

https://docs.anthropic.com/en/docs/claude-code/slash-commands#custom-slash-commands

ユーザー空間 or プロジェクト空間に .claude/commands/*.md ファイルを作るだけでコマンドを生やすことができます。

/compact じゃだめなの?

Claude Codeにはビルトインで /compact コマンドがあり、ここまでの会話を圧縮することができます。

https://docs.anthropic.com/en/docs/claude-code/slash-commands#built-in-slash-commands

これでも良いといえば良いのですが、以下の点が気になりました。

まず、コンテキストから捨ててもいい(むしろ捨てたい)試行錯誤の会話を含めたくないというのがあります。 /compact ではこれが含まれる可能性があります。「Aの方針で」「あ、うまくいかなかったからやっぱり真逆のBの方針で」という二転三転の会話履歴を圧縮すると矛盾したストーリーが残りLLMを混乱させかねません。この点、 git commit を確定させた変更点は暫定の確定事項であり与えべきコンテキストのエッセンスになっていると考えました。

また、これはメインの理由ではありませんが、 /compact コマンドが不安定で、ところどころバグる気がします。 /compact を待っている間に追加の指示を出したらターミナルがチラつきまくって操作不能になることが複数回ありました。

手順

/analyze-diff コマンド作成

以下のようなカスタムスラッシュコマンドを作成しました。ユーザー空間に作成する例です。チームメンバーがOKしてくれるならプロジェクト以下に作ってgit管理しても良いでしょう。

~/.claude/commands/analyze-diff.md
## 0. 変数定義

以下の擬似言語に従って $TARGET_BRANCH 変数を定義してください。

```
if ($ARGUMENTS == null) {
  // 引数がない場合はデフォルトブランチを格納
  TARGET_BRANCH=$(git remote show origin | grep "HEAD branch" | cut -d' ' -f5)
} else { 
  TARGET_BRANCH=$ARGUMENTS
}
```

## 1. コードの最新化

現在のブランチに $TARGET_BRANCH リモートブランチの最新コードをマージしてください。

```bash
git fetch
git merge origin/$TARGET_BRANCH
```

## 2. ブランチの差分を分析する

origin/$TARGET_BRANCH ブランチと現在のブランチの差分を `git diff` で分析して、

- 全体的な仕様の変更概要
- 各ファイルの変更

を出力してください。

JSなのかBashなのかよくわからない擬似言語ですが $TARGET_BRANCH 変数定義を指示しています。スラッシュコマンドを呼ぶときに /my-command arg で与えた引数はコマンドファイル内で $ARGUMENTS で取り出せるようになっています(取り出せるというか、そう解釈するようにシステムプロンプトで指示されているだけだと思いますが)。

引数を渡した場合はそのブランチを、引数がない場合はデフォルトブランチを格納します。ついでにターゲットブランチの最新を現在のブランチに取り込んでしまいます。これは人によっては嫌かもしれませんが自分はこまめに取り込むのが好きなので入れています。

そして本筋の処理です。ターゲットブランチと現在のブランチの差分を比較させます。差分を分析させて、全体的な仕様の変更概要と各ファイルの変更を喋らせます。わざわざ出力させることで以降のやり取りに役立つコンテキストとして残せます。

実際使っていますが、今のところまあまあいい感じに機能しているように見えます。

Permissionを設定

必要に応じてユーザーもしくはプロジェクトの settings.json settings.local.json に以下の permissions.allow を追加しておくと自動運転が捗ります。

~/.claude/settings.json
{
  "permissions": {
    "allow": [
      // ...
      "Bash(git diff:*)",
      "Bash(git fetch)",
      "Bash(git merge origin/:*)",
      // ...
    ],
  }
}

以上、参考になれば幸いです。

Discussion