同じ指示を毎回書かない ─ Claude Code Skills 入門

「もし、よく使うプロンプトをコマンド一発で呼び出せたら便利だと思いませんか?」

Claude Code を使い始めて 2 ヶ月、3 ヶ月と経つと、私はあることに気づきました。同じプロンプトを 3 回目、4 回目とコピペしている自分がいるのです。

「未コミットの変更を要約して、リスクがあったら指摘して」
「このコミット、スタイルガイドに沿った日本語のメッセージにして」
「PR のレビューを、まず diff を全部読んでから始めて」

そのたびに過去のチャット履歴を漁り、適当に文面を整えて貼り付けます。せっかく動いているプロンプトの再現性は、私の脳のメモリに頼っています。当然、別の PC では再現できないし、チームメンバーにも渡せません。

LLM 自体は半年単位で進化しています。なのに、私の使い方は半年前と何も変わっていません。これはおかしいぞ、と思うわけです。

この記事では、その違和感の正体と、Anthropic が用意した処方箋である Skills について書きます。

問題の立て方が間違っている

「使い回したいプロンプトをどう保存するか?」と考えている時点で、たぶん問題の立て方が違います。

正しい問いはこうです。

「使い回したい手順を、どうやって Claude Code の道具にするか?」

プロンプトは「文字列」です。Skill は「道具」です。文字列はコピペが基本ですが、道具は呼び出すものです。/commit/summarize-changes/deploy のように、/ から始まる名前で呼び出せば、いつもの手順がそのまま走ります。

私は最初、CLAUDE.md にすべて押し込もうとしていました。「コミットメッセージは日本語で書く」「テストは pytest を使う」「ライブラリの追加前に確認する」など、ルールを足すたびに CLAUDE.md は分厚くなっていきます。

CLAUDE.md は確かに便利な道具ですが、実は書く内容の種類が違うものを一緒くたにしていました。

書く場所中身性質
CLAUDE.mdプロジェクトの「前提情報」常に context に常駐させるべき情報
Skill再利用したい「手順」呼ばれたときだけ context に入れたい情報

前提情報とは、Claude が何かを書くたびに頭に入っていてほしいものです。「このリポは monorepo(フロントエンド・バックエンド・共有ライブラリなどを 1 つの git リポジトリにまとめる構成)」「テストランナーは pytest」「DB は PostgreSQL」など。

コードを書くときも、コミットメッセージを書くときも、レビューするときも、これらが分かっていないと話が始まりません。だから CLAUDE.md に書いて、毎回 Claude の頭に入った状態でセッションを始めます。

一方、手順は違います。/deploy の中身を、コミットメッセージを書いている最中まで Claude の頭に入れておく理由はありません。呼ばれたときだけ context に入ってくれればいいのです。

進化させるべきは私の記憶ではなく、私の道具でした。

なぜ「全部 context に入れる」だと壊れるのか

context window は有限です。Claude Code はこの有限な箱に、システムプロンプト、CLAUDE.md、現在のファイル、過去のやり取りをすべて詰め込みます。ここに「使うかも分からない手順書」を全部入れたら、肝心の作業のための余白が無くなります。

そこで Anthropic は、Skills に progressive disclosure(段階的開示) という仕組みを組み込みました。

sequenceDiagram participant U as ユーザー participant C as Claude Code participant S as Skill 本体 Note over C: セッション開始時 C->>C: skill description だけ context に常駐<br/>(本文は読まない) U->>C: 「未コミットの変更を要約して」 C->>C: description にマッチする skill を発見 C->>S: 該当 skill の本文をロード S-->>C: SKILL.md 本文を context に追加 C->>U: 手順に従って実行 Note over C: 以降、その skill 本文は<br/>セッション中ずっと残る

公式 docs には次のように書かれています。

“skill descriptions are loaded into context so Claude knows what’s available, but full skill content only loads when invoked.”
「スキルの説明はコンテキストに読み込まれるため、クロードは何が利用可能かを知っていますが、完全なスキルの内容は呼び出されたときにのみ読み込まれます。」

つまり、「何ができる skill か」は常時知っているが、「どうやるか」の本文は呼ばれるまで読まない。これが progressive disclosure です。

副次的な効果もあります。description の枠は限られているので、自然と「この skill は何をするものか」を 1 行でハッキリ書く規律が生まれます。冗長な能書きの居場所がそもそも無いのです。

Character Budget の現実

description は context に常駐するので、当然 context を食います。公式 docs に明記されている数字は次の通りです。

  • skill description 全体に割り当てられる予算は 「context window の 1%、フォールバックは 8,000 文字」
  • 個別の skill について、descriptionwhen_to_use を合わせて 1,536 文字で打ち切られる

予算オーバーで切り詰められるのは description だけで、skill 名そのものは常に context に入っています。なので私が /skill-name と打って明示的に呼び出せばちゃんと動きます。

影響を受けるのは Claude が会話の流れから自動で発火させる「auto-discovery(自動呼び出し)」のほうです。description を 200 字、300 字と書いていると、末尾の重要キーワードがちょん切られて、Claude が「今がこの skill を呼ぶタイミング」と気づかなくなります。

公式 docs の該当セクションの言い回しはこうです。

“All skill names are always included, but if you have many skills, descriptions are shortened to fit the character budget, which can strip the keywords Claude needs to match your request.”
「すべてのスキル名は常に含まれますが、多くのスキルを持っている場合、説明はキャラクターの予算に合わせて短縮されるため、クロードがリクエストに一致するために必要なキーワードが削除される可能性があります。」

つまり、明示呼び出しは生きていて、自動呼び出しが鈍くなる。「/deploy のように手で叩く前提の skill」なら影響は小さく、「自然な会話の中で Claude に拾わせたい skill」ほど description の trigger キーワード設計が効きます。

予算を増やしたい場合は環境変数 SLASH_COMMAND_TOOL_CHAR_BUDGET で上書きできます(同 docs の同じセクションに記載あり)。

自己診断: あなたが踏みやすい 5 つの罠

ここまで読んだうえで、自分を当てはめてみてください。

罠1: CLAUDE.md にすべて詰め込む

CLAUDE.md が 200 行を超えたら危険信号です。コミュニティの整理(shanraisshan/claude-code-best-practice の CLAUDE.md)でも「Keep CLAUDE.md under 200 lines per file for reliable adherence」という運用知見が共有されています。

200 行を超え始めたら、その中身は前提情報だけでしょうか。たぶん、手順が混ざっています。手順は Skill に切り出してください。

罠2: 同じ指示を毎回チャットに書く

「PR の diff を読んで、変更点を 3 行でまとめて、リスクがあれば指摘して」を 3 回目に書いている自分を発見したら、それは Skill にするタイミングです。3 回はサインです

罠3: description を冗長に書く

「この skill は xxx という場面で yyy のようにして zzz してくれて、さらに aaa にも使えて…」と書きたくなる気持ちは分かります。でも 1,536 文字の cap に当たった瞬間、後ろの「さらに aaa にも使えて」は context から消えます。

最初に「何をする skill か」を一文で言い切る。その後ろに状況補足を付ける。これだけで Claude の trigger 精度は上がります。

罠4: 副作用のある skill に invocation 制限を付け忘れる

Claude Code は description を読んで「今がこの skill を呼ぶタイミング」と判断したら、勝手に呼びます。これが厄介になるのは 副作用のある skill です。

/deploy/send-slack-message/git-push のように、世界に何か影響を与える skill は、必ず frontmatter に disable-model-invocation: true を書いてください。これを書くと「ユーザーが /deploy と打ったときだけ動く、Claude は勝手に呼ばない」状態になります。

公式 docs の該当セクションから:

“Use this for workflows with side effects or that you want to control timing, like /commit, /deploy, or /send-slack-message. You don’t want Claude deciding to deploy because your code looks ready.”

身に覚えのある言い方ですよね。「コードが整ったから Claude がデプロイしました」は、私たちが望むやつではありません。

罠5: チームに渡そうとして個人スコープに置く

skill には 4 つの置き場所があります。

flowchart TD A[Skill の置き場所] --> B[Enterprise<br/>managed settings] A --> C["Personal<br/>~/.claude/skills/"] A --> D[".claude/skills/<br/>プロジェクト直下"] A --> E["Plugin<br/>plugin/skills/"] B -.同名衝突時 最強.-> F["同名なら<br/>強いほうが勝つ"] C -.同名衝突時 2番.-> F D -.同名衝突時 3番.-> F E -.別名前空間.-> G["plugin-name:skill-name で衝突回避"]

公式 docs の “Where skills live” に明記されている 4 種類です。同じ名前の skill が複数の場所に置かれていた場合、enterprise が personal を上書きし、personal が project を上書きします(図の点線ラベルはこの上書き順を示しています)。plugin だけは plugin-name:skill-name という名前空間に入るので、他と名前が衝突しません。

ここでよくある間違いは、チーム全員に使ってほしい skill を ~/.claude/skills/ に置いてしまうことです。これは自分のホームディレクトリにしか入っていないので、チームメンバーの環境では動きません。

チームで共有したいなら プロジェクト直下の .claude/skills/ に置いて、git にコミットします。これでリポジトリを clone した全員が同じ skill を使えます。

monorepo を使っている場合は、もう一段階の最適化があります。packages/frontend/.claude/skills/ のような ネストした場所に置いた skill は、Claude Code がそのパッケージのファイルを編集しているときだけ自動的に discover されます(同じく公式 docs に記載)。フロントエンドの skill が、バックエンドだけ触っているときに context を食わずに済むわけです。

処方箋: 最初の Skill を作る 7 ステップ

罠を避けるための具体策です。最初は最小から始めて、必要に応じて拡張します。

ステップ1: 最小構成で動かす

公式 docs のクイックスタートからそのまま引いた、最小の skill です。

mkdir -p ~/.claude/skills/summarize-changes

そしてそのディレクトリに SKILL.md を作ります。中身は次の通りです。これだけで /summarize-changes というコマンドが Claude Code に追加されます。

---
description: 未コミットの変更を要約し、リスクのある箇所を指摘します。「何を変更した?」「コミットメッセージ案を出して」「diff をレビューして」と聞かれたら使ってください。
---
## 現在の変更
!git diff HEAD## 手順
上記の変更を 2〜3 個の箇条書きで要約し、エラーハンドリング漏れ・ハードコード・テスト更新漏れなど気になる点を列挙してください。diff が空なら「未コミットの変更はありません」と答えてください。

冒頭の !git diff HEAD が動的コンテキスト注入です。skill が呼ばれた瞬間にこのコマンドが実行され、その出力が本文に差し込まれた状態で Claude に渡ります。Claude は git diff を実行する必要すらありません。結果がもう手元に来ている状態でスタートします。

ステップ2: frontmatter は最初 description だけでよい

skill の frontmatter には 15 個のフィールドが用意されています(公式 docs の Frontmatter reference に全フィールドの表があります)。

flowchart LR F["SKILL.md frontmatter"] --> A["必須相当<br/>description"] F --> B["挙動制御<br/>disable-model-invocation<br/>user-invocable<br/>allowed-tools"] F --> C["実行環境<br/>model<br/>effort<br/>context: fork<br/>agent"] F --> D["入力<br/>argument-hint<br/>arguments"] F --> E["拡張<br/>name<br/>hooks<br/>paths<br/>shell<br/>when_to_use"]

公式 docs には 15 フィールドの一覧表があり、コミュニティの整理(shanraisshan の Skills Best Practice)でも同数の表が公開されています。一覧として眺めるならどちらかを参照してください。

最初は description だけ書けば動きます。残りは「特定の挙動が欲しくなったときに足す」で十分です。

ステップ3: 副作用のある skill に invocation 制限を付ける

罠4 で書いた通りです。/deploy のような skill には必ず付けてください。

---
name: deploy
description: 本番環境にデプロイします
disable-model-invocation: true
---
$ARGUMENTS を本番にデプロイします:
1. テストスイートを走らせる
2. ビルドする
3. デプロイ先に push する
4. デプロイ成否を確認する

disable-model-invocation: true を入れると、私が /deploy と打ったときだけ走ります。Claude が空気を読んでデプロイを始めることはありません。

ついでに allowed-tools を付ければ、その skill が active な間に必要なツールを事前承認できます。これは「skill が走るたびにいちいち許可ダイアログが出る」のを防ぐためのフィールドで、公式 docs の “Pre-approve tools for a skill” に詳しく書いてあります。

ステップ4: 動的コンテキスト注入を活用する

ステップ1 の !git diff HEAD は、skill が呼ばれた瞬間に外部コマンドを実行し、その出力を skill 本文に差し込んでくれる仕組みです。これは Claude のツール呼び出しではなく、skill の本文が Claude に渡る前に行われる前処理です。

複数行の場合は !</code> で始まるフェンス付きブロックを使います。</p> <!-- /wp:paragraph --> <!-- wp:enlighter/codeblock --> <pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">## Environment! node --version npm --version git status --short</pre> <!-- /wp:enlighter/codeblock --> <!-- wp:paragraph --> <p>PR レビューならこんな skill が組めます(<a href="https://code.claude.com/docs/en/skills#inject-dynamic-context">公式 docs の "Inject dynamic context" の例</a>から引用、日本語ベースに調整)。</p> <!-- /wp:paragraph --> <!-- wp:enlighter/codeblock --> <pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">--- name: pr-summary description: プルリクエストの変更を要約します context: fork agent: Explore allowed-tools: Bash(gh *) --- ## PR コンテキスト - diff: ! pr diff- コメント: ! pr view --comments- 変更ファイル: ! pr diff --name-only## あなたのタスク このプルリクエストを要約してください... </pre> <!-- /wp:enlighter/codeblock --> <!-- wp:paragraph --> <p><code>gh pr diff</code> を Claude に頼まなくても、もう手元に diff がある状態でレビューが始まります。</p> <!-- /wp:paragraph --> <!-- wp:heading {"level":3} --> <h3 class="wp-block-heading">ステップ5: subagent で fork する条件を判断する</h3> <!-- /wp:heading --> <!-- wp:paragraph --> <p><code>context: fork</code> を frontmatter に書くと、その skill は <strong>メイン会話と切り離された subagent の中で</strong>実行されます。会話履歴を持っていきません。</p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p>長い調査をしたいとき、本筋の context を汚したくないとき、あるいは大量のファイルを grep したいときに有効です。skill 本文がそのまま subagent への指示になります。</p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p>注意点として、公式 docs に明記されている通り、 <code>context: fork</code> は <strong>明確なタスク指示を持つ skill にしか向きません</strong>。「このリポの API 規約」のような参照系コンテンツに <code>context: fork</code> を付けると、subagent は規約だけ受け取って何も指示が無い状態で起動するので、結局何も返さずに終わります。</p> <!-- /wp:paragraph --> <!-- wp:heading {"level":3} --> <h3 class="wp-block-heading">ステップ6: skill の置き場所を決める</h3> <!-- /wp:heading --> <!-- wp:paragraph --> <p>罠5 で書いた 4 種類です。判断は単純です。</p> <!-- /wp:paragraph --> <!-- wp:list --> <ul class="wp-block-list"><!-- wp:list-item --> <li>自分だけが使いたい → <code>~/.claude/skills/</code></li> <!-- /wp:list-item --> <!-- wp:list-item --> <li>チームで共有したい → プロジェクト直下の <code>.claude/skills/</code> に置いて git コミット</li> <!-- /wp:list-item --> <!-- wp:list-item --> <li>配布パッケージにしたい → plugin 化して <code>plugin/skills/</code></li> <!-- /wp:list-item --> <!-- wp:list-item --> <li>組織全体で強制したい → enterprise の managed settings</li> <!-- /wp:list-item --></ul> <!-- /wp:list --> <!-- wp:paragraph --> <p>私は最初、何でも <code>~/.claude/skills/</code> に置いていましたが、後で「あ、これチームで共有したかった」となって移動するハメになりました。<strong>最初から目的に合った場所に置いた方が、移動の手間が省けます</strong>。</p> <!-- /wp:paragraph --> <!-- wp:heading {"level":3} --> <h3 class="wp-block-heading">ステップ7: 公式の bundled skills を眺める</h3> <!-- /wp:heading --> <!-- wp:paragraph --> <p><a href="https://code.claude.com/docs/en/skills#bundled-skills">公式 docs</a> には、Claude Code が標準で備える bundled skills として <code>/simplify</code>、<code>/batch</code>、<code>/debug</code>、<code>/loop</code>、<code>/claude-api</code> の 5 つが明記されています(コミュニティの集計では 6 つ目として <code>/fewer-permission-prompts</code> が含まれることがありますが、公式 docs に名前の記載がないため、本記事ではあくまで「公式 docs に明記された 5 個」として扱います)。</p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p>これらは「LLM へのプロンプトに翻訳された」skill で、固定ロジックではなく、Claude が指示を読み解いて自分のツールで実行します。<code>/simplify</code> は「変更されたコードを再利用と効率の観点でレビューして整理する」、<code>/loop</code> は「定期的にプロンプトを再実行する」、<code>/claude-api</code> は「Anthropic SDK のコードを書くときに発火する」といった具合です。</p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p>自分で skill を書く前に、これらが既に何をカバーしているかを <code>/help</code> で眺めておくと、車輪の再発明を避けられます。</p> <!-- /wp:paragraph --> <!-- wp:heading --> <h2 class="wp-block-heading">個人的告白</h2> <!-- /wp:heading --> <!-- wp:paragraph --> <p>ここまで偉そうに書きましたが、何を隠そう、私もまだ skill を使い切れているとは言えません。CLAUDE.md と skill の境界線を、今もしばらく試行錯誤しています。</p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p>「これは前提情報だから CLAUDE.md か?」「いや、これは手順だから skill か?」「両方の中間にあるものはどっちに置く?」と、毎週のように小さな移動を繰り返しています。</p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p>でも、ひとつだけハッキリしたことがあります。<strong>自分が同じプロンプトを 3 回コピペしている瞬間は、それを skill にすべきサインだ</strong>ということです。3 回目に「あれ、また書いてるな」と感じたら、その手を止めて、<code>mkdir ~/.claude/skills/...</code> から始めてください。</p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p>その小さな一歩で、あなたの Claude Code は「いつもの作業をいつものように呼べる道具」に変わります。プロンプトを文字列としてではなく、道具として扱う。これだけで、半年前の自分を確実に追い越せます。</p> <!-- /wp:paragraph --> <!-- wp:heading --> <h2 class="wp-block-heading">TL;DR</h2> <!-- /wp:heading --> <!-- wp:list --> <ul class="wp-block-list"><!-- wp:list-item --> <li><strong>同じプロンプトを 3 回コピペ</strong>しているなら、それは Skill にすべきサイン</li> <!-- /wp:list-item --> <!-- wp:list-item --> <li>CLAUDE.md は「前提情報」、Skill は「手順」を書く場所</li> <!-- /wp:list-item --> <!-- wp:list-item --> <li>progressive disclosure: description は常駐、本文は invoke 時にロード(context window の 1% / fallback 8,000 文字、個別 skill は description+when_to_use で 1,536 文字 cap)</li> <!-- /wp:list-item --> <!-- wp:list-item --> <li>副作用のある skill には必ず <code>disable-model-invocation: true</code> を付ける</li> <!-- /wp:list-item --> <!-- wp:list-item --> <li>チーム共有は <code>.claude/skills/</code> を git コミット、個人用は <code>~/.claude/skills/</code>、monorepo は package 直下の <code>.claude/skills/</code> で nested discovery</li> <!-- /wp:list-item --> <!-- wp:list-item --> <li>動的コンテキスト注入(<code>!`)で外部状態を skill 本文に差し込む

  • 公式 bundled skills は /simplify/batch/debug/loop/claude-api の 5 個(公式 docs に明記)