メインコンテンツへスキップ
kt-tech.blog

【設計】Claude Code の skill と slash command の境界線 — 反復作業をどちらに寄せるか(連載 第4回)

設計17分で読めます

この記事でわかること

  • command と skill は同じ Markdown + frontmatter なのに、何をどちらに書くべきかは曖昧なままになりがちです。実運用している 9 つの command と 4 つの skill を題材に、手順の段数・リソース同梱の要否・破壊性という 3 つの判断軸で境界線を引きます。あわせて allowed-tools の正体(ツール制限ではなく、そのターン限りの事前承認)と、本当に制限したいときの disallowed-tools / permissions.deny / subagent の tools の使い分けを整理します。連載最終回。

Claude Code の Skills ドキュメントを一度眺めていること。連載第1回で扱った「.claude/ を土台・ガード・拡張の3層で見る」という前提を共有していると読みやすいですが、本記事単体でも読めます。

概要: 連載の最終回です。拡張層の残り 2 つ「slash command」と「skill」を取り上げます。両者は似て見えますが、ちゃんと区別しないと「/foo と打ったときに何が起きるか予測できない .claude/」になります。本記事では「どちらに寄せるか」の判断軸、disable-model-invocation の使いどころ、allowed-tools が実際には何をするのか(そして何をしないのか)までを整理します。

メモ
2026 年 8 月時点の補足: custom commands は skills に統合され、両者は同じ frontmatter を受け付けます。本記事の区別は運用上の設計指針として読んでください。

はじめに

Claude Code の拡張層は 3 つあります。

  • agents/ — 独立 context を持つ subagent(連載第 3 回)
  • commands/ — slash command。/lint のようにショートカットとして実行される
  • skills/ — 多段手順 + 文脈を束ねた playbook

このうち commands / skills は、両方とも .md ファイル + frontmatter で書きます。見た目が似ているだけに、使い始めると 「これは command にしたほうがいいのか skill にしたほうがいいのか」 でその都度迷うことになります。そこを雑にさばくと、チームメンバーが /foo を打ったときに「今何が起きるのか」が予測できなくなります。

本記事では、実運用している 9 つの command と 4 つの skill を題材に、境界線と運用コツを整理します。

Skills そのものの入門は別記事 Claude Code Skills 入門 で書いています。本記事は「command と skill の使い分け」に焦点を絞ります。


1. command と skill は何が違うか

まず見た目ではなく「起動モデル」で比較します。

slash command skill
起動方法 ユーザーが /foo と打つ — 明示 明示もしくはモデルが description を見て自動起動
よくある使い方 「1 つのコマンド + 軽い指示」 「多段手順 + 判断ロジック」
ファイル構造 .claude/commands/foo.md 1 ファイル .claude/skills/foo/SKILL.md と追加リソース
代表例 /lint/fmt/test-api pipeline-trace 、release-notes 、azurite-reset
! プレフィックス 使うとその行がシェルとして実行 同じように使える
モデル自動呼び出し なし(ユーザー明示のみ) 有り(disable-model-invocation: true で off)

一番重要な違いは最後の 1 行です。skill は description マッチでモデルが自動トリガーできる、という点で subagent に似ています。command は「ユーザーが明示で叩く」以外の起動経路がありません。

この違いが、後述する「どちらに寄せるか」の判断ロジックに直結します。

2. command のやること — 薄いラッパーに留める

中規模のプロジェクトで実際に使っている command ファイルはこういう顔ぶれです。

コマンド 中身の要点
/lint make lint を走らせて、失敗なら file:line を拾って修正提案
/fmt make fmtgit diff --stat
/test-api cd … && poetry run pytest -x $ARGUMENTS
/db-migrate alembic revision --autogenerate -m "$ARGUMENTS"
/up /down make up / make down
/seed make seed、失敗時は README へ誘導
/pr-summary git diff --statgit log を拾って PR 本文を作る
/verify-no-prod-touch live env ディレクトリと destructive Alembic op を grep

どれも「よく使うシェルコマンド + 軽い指示」のショートカットでしかないことに注目してください。中身はせいぜい 5 〜 15 行、多段の判断ロジックは入りません。逆に、多段の手順や分岐を持つ重いものは skill 側に寄せます。

command のテンプレート

例として /lint だとこういう中身です。

Markdown
---
description: Run ruff lint + format check on the API package.
allowed-tools: Bash(make:*), Bash(poetry:*), Bash(ruff:*)
---

Run the project lint:

!`make lint`

If anything fails, summarize the diagnostics and propose specific fixes referencing file:line. Do not auto-format unless the user asks.

frontmatter はたった 2 フィールド、本文は 1 コマンド + 補足指示だけです。これが command のちょうどいい重さです。

! プレフィックスの動き

本文中でバックティック付きの !... と書くと、その行がシェルとして実行され、出力がその位置に挬られます。/pr-summary のように 3 つ 並べれば、実行結果をモデルの手元に揃えた状態で、周辺の指示を書けます。

Markdown
Gather the change set:

- Diff stat: !`git diff --stat origin/develop...HEAD`
- File list: !`git diff --name-only origin/develop...HEAD`
- Commit log: !`git log origin/develop..HEAD --oneline`

Produce:

1. **Title** — under 70 chars, conventional-commit style.
2. **Summary** — 1-3 bullets describing *what* and *why*.

このパターンは「その場で使うコンテキストを手元に揃えておいて、モデルの生成はその上でやらせる」という設計です。command の仕事の中でもとくに効きます。

argument-hint と $ARGUMENTS

argument-hint を frontmatter に書くと、UI で /foo を打ったときに次の引数が何かをヒントとして見せてくれます。本文側で $ARGUMENTS と書けば、ユーザーが渡した文字列に置換されます。

Markdown
---
description: Run the FastAPI test suite via poetry.
argument-hint: "[pytest-args]"
allowed-tools: Bash(poetry:*), Bash(pytest:*)
---

Run the API test suite (pass-through args: $ARGUMENTS):

!`cd path/to/api && poetry run pytest -x $ARGUMENTS`

/test-api tests/test_foo.py::test_bar のように pytest のパススルー引数を渡せるようになります。

3. skill のやること — 多段手順と判断ロジックを束ねる

同じプロジェクトの skill を見ると、command とは明らかに重さが違うことがわかります。

skill 何をやるか
pipeline-trace 1 つのドキュメントを multi-stage pipeline に流して、どこで詰まったかを切り分ける
release-notes git log をカテゴリごとにグループして changelog を作る
customer-slide-narrative スライドの PDF + git diff から事実ベースの補足文と図解プロンプトを生成する
azurite-reset ローカル DB とストレージエミュレータの状態を破壊して seed し直す(破壊的)

どれも 1 コマンドでは済まず、「この順番でこれを見て、何があったら次にこうする」というロジックが中身に入ります。

skill のテンプレート

例として pipeline-traceSKILL.md はこんな骨格です。

Markdown
---
name: pipeline-trace
description: Trace a multi-stage pipeline run end-to-end against the local docker-compose stack. Use when a document upload does not produce expected output and you need to localise the failure to a specific stage.
allowed-tools: Bash(make:*), Bash(docker:*), Bash(docker compose:*), Bash(curl:*), Bash(jq:*), Read, Grep
---

# Pipeline trace

You will follow one document through the integrated pipelines container.

## Pre-flight
Confirm the stack is up with `make ps`.
If any service is down, run `make up` and wait for healthy.

## Step 1 — stage 1 (ingest)
- List blobs in the input container.
- Trigger the endpoint with curl.
- Check the output container for the expected artifact.
- If missing, look at logs for the ingest service.

## Step 2 — stage 2 (transform)
... 以下同様に手順を並べる。

## Troubleshooting
- If the classifier returns empty tags, the LLM prompt is fine; check the upload metadata.
- If a blob is in quarantine, check the move_to_quarantine call site.

形式としては command と同じ Markdown + frontmatter ですが、中身は「人間に渡すトラブルシュート」のスタイルで書きます。「こうしたらこうする」「これが見えたらこちら」という判断分岐を明示するのがポイントです。

skill はディレクトリを持てる

skill は .claude/skills/foo/SKILL.md という位置に置きます。つまり foo/ というディレクトリを持てるので、補助スクリプト・テンプレート・リファレンス資料を同梱できます。release-notes に changelog テンプレートを同梱したり、customer-slide-narrative にスライドテンプレを同梱したり、という拡張が可能です。command ではできません。

4. 「どちらに寄せるか」の 3 つの判断軸

ここから本題です。迷ったときはこの 3 つを順に見ればまとまるはずです。

軸 1: 手順は何段か

  • 1 〜 2 コマンドで済む → command
  • 3 段以上、判断分岐がある → skill

make lint は 1 コマンドで終わり、分岐は「出た警告をどう説明するか」だけなので command。一方 pipeline のトレースは「ステージ 1 の出力がある/ない」「ステージ 2 は実行されたか」と分岐が何段もあるので skill。

軸 2: リソースを同梱したいか

  • 本文だけで足りる → command
  • テンプレート、サンプル、補助スクリプトが要る → skill

skill はディレクトリを持てるので、SKILL.md 以外のファイルも一緒に読ませたり、補助スクリプトを同梱したりできます。「多段手順」でさえ本文だけで足りるなら command でも構いません。

軸 3: 破壊的か / 明示呼び出し限定にしたいか

  • 破壊的、モデルが勝手に走らせると困る → skill + disable-model-invocation: true
  • それ以外 → command または通常の skill

3 つ目が一番シビアな軸で、次節で詳しく見ます。

5. disable-model-invocation: true を使うべきケース

skill の frontmatter にこの一行を加えると、モデルはこの skill を自動で起動できなくなり、ユーザーが明示で呼んだときだけ動くようになります。

YAML
---
name: azurite-reset
description: Wipe local DB rows and Azurite blobs, then re-seed. Destructive  only invoke when the user explicitly asks.
allowed-tools: Bash(make:*), Bash(docker:*), Bash(docker compose:*)
disable-model-invocation: true
---

これを使うべき典型例は 3 つあります。

破壊的な作業。ローカル DB と Blob の wipe、git reset --hard、Docker volume の削除など。「モデルが『似たような記述を見つけて』勝手に呼んだ」という事故を起こさないため、このフェイルセーフを付けます。

人間レビューを必須にしたい作業。出力をそのまま外部に出すものや、コストがかかる作業。「うっかり走っちゃう」と本当に困るものは、明示呼び出し限定で人間の意思決定を挏みます。

狭いドメインに限定したい作業。例えば「特定顧客への資料作成」のようなものは、たまたまユーザーが似た話をしたからといって勝手に走られると困るので、同じく明示限定にします。

逆に pipeline-tracerelease-notes は「モデルが便利だと思ったら勝手に使ってもらって OK」なので、disable-model-invocation は付けません。

6. allowed-tools は「制限」ではなく「事前承認」

ここは筆者が長らく誤解していた部分なので、はっきり書いておきます。frontmatter の allowed-tools は、使えるツールを制限するものではありません。列挙したツールに対して、そのターンのあいだ承認プロンプトなしで使える権限を与えるだけです。公式ドキュメントの記述はこうです。

The allowed-tools field grants permission for the listed tools during the turn that invokes the skill, so Claude can use them without prompting you for approval. The grant clears when you send your next message... It does not restrict which tools are available: every tool remains callable, and your permission settings still govern tools that are not listed.

YAML
allowed-tools: Bash(make:*), Bash(poetry:*), Bash(ruff:*)

この例が意味するのは「make / poetry / ruff は確認なしで走らせてよい」であって、「それ以外の Bash は禁止」ではありません。/lint の実行中に Claude が git status を見ようとしたら、拒否されるのではなく、通常の permissions に従って(必要なら承認プロンプトを出して)実行されます。つまり allowed-tools だけでは脱線は防げません。与えた権限が次のメッセージを送った時点で失効する点も押さえておきます。

使い方は次の 2 つに整理できます。

承認プロンプトを減らすために書く。定型作業のたびに「make lint を実行してよいですか」と聞かれるのを止めるのが本来の目的です。逆に言うと、粒度を粗くしてはいけませんBash のようにツール名だけを書くと、その command / skill を叩いたときだけ任意のシェルコマンドが無確認で走る状態になります。プロジェクトの .claude/skills/ に置いた skill はそのリポジトリを信頼した時点で有効になるので、他人の書いた skill の allowed-tools は読んでから信頼してください

人間へのドキュメントとしても効く。allowed-tools を読めば「この command が想定しているコマンドはこれだけ」がわかります。チームメンバーが他人の command を読んだとき、中身の見込みが一気につきます。あくまで宣言であって強制ではない、という前提だけチームで共有しておけば有用です。

本当に「使わせない」を実現する手段

副作用を本当に絞りたいときに使うのはこちらです。

手段 効き方 スコープ
allowed-tools 列挙したツールを事前承認する。制限はしない 呼び出したターンのみ
disallowed-tools(skill の frontmatter) 列挙したツールを Claude の利用可能プールから取り除く その skill が有効な間(次のメッセージで解除)
permissions.deny(settings.json) 使用を禁止する。ツール名を裸で書くと Claude の context からツールごと消える セッション全体
subagent の tools:.claude/agents/*.md 本物の allowlist。列挙外は使えない その subagent の実行中

pipeline-trace のような読み取り中心の skill で「間違っても書き込ませない」を担保したいなら allowed-tools ではなく disallowed-tools を、リポジトリ全体で禁止したいなら permissions.deny を使います。skill 側でできるもう一つの安全策は、前節の disable-model-invocation: true で「勝手に起動されない」ようにすることです。

7. 「/foo を叩くと何が起きるか」を予測可能にする

最後に、command / skill をチームで長期運用するための設計原則を 1 つだけ。

「/foo を叩いたら何が起きるか」が予測できる状態を保つ

  • command は「そのシェルコマンド + 軽い助言」だけ、という予測を保つ
  • skill は「この SKILL.md を読み、その手順に従う」という予測を保つ
  • 両者を混ぜない — command の中で「状況に応じて 5 段適応」は書かない、skill の中で「1 行だけ」にはしない

この予測可能性が衰えると、チームメンバーは / を叩くのをやめます。.claude/ の拡張層が誰も触らない飾りになるか、日々使われるツールになるかは、この「予測可能性」をどこまで守れるかに掛かっています。


Tips

  • 「迷ったら command」をデフォルトにしない。多段手順を command に詰め込むと、何が起きるか予測しづらいショートカットになる
  • ! プレフィックスは command の最大の武器。シェル出力をその位置に挬らめるので、「diff を見てコメントしろ」という仕事と相性がよい
  • disable-model-invocation は選ぶべきときに選ぶ。付けすぎると skill の価値が下がるし、付けなさすぎると事故につながる
  • allowed-tools は事前承認であって制限ではない。「とりあえず Bash フル許可」と書くと、その command を叩いたときだけ無確認で何でも走る状態になる。制限したいなら disallowed-toolspermissions.deny、subagent の tools:
  • skill はディレクトリを持てることを生かす。テンプレートやサンプルを同梱して「これに沿って説明して」と言えると、skill の価値が一段上がる

まとめ

  • command は「1 コマンド + 軽い指示」のショートカット、skill は「多段手順 + 判断ロジック」の playbook
  • 迷ったら 3 つの軸で見る: 手順の段数 / リソース同梱の必要性 / 破壊的か
  • 破壊的・明示呼び出し限定にしたい skill には disable-model-invocation: true を付ける
  • allowed-tools事前承認であって制限ではない。副作用を本当に絞りたいなら disallowed-toolspermissions.deny、subagent の tools:
  • 「/foo を叩いたら何が起きるか」の予測可能性を守ることが長期運用の鍵

連載を振り返って

全 4 回で話したことをシンプルにまとめるとこうです。

Claude Code のハーネスエンジニアリングは「個人の便利ツール」を「チームの標準装備」に変える作業です。事故を下層で止め、作業を上層で型化し、その間を人間の意思決定と LLM の柔軟さで埋めていく — という設計思想が、連載を通して伝わっていれば嬉しいです。

参考リンク

更新履歴

  1. 連載第1〜3回へのリンクが Notion の内部 URL のままで読者が開けなかったのを、ブログの記事 URL に修正。公式ドキュメントへのリンクを現行のドメインに更新。変換ミスの修正(劃し→区別し、挫める→留める、テンプレト→テンプレート、挬られ→差し込まれ、一泛させた→揃えた、ホント→ヒント、ナレージ→カテゴリ、めとめまる→まとまる、フォールドセーフティ→フェイルセーフ、保動体→飾り など)。本文のファイル名表記が誤ってリンク化されていた箇所をインラインコードに変更。「この記事でわかること」「対象読者」「前提条件」を概要プロパティへ移動。
  2. §6 を全面差し替え。allowed-tools はツールを制限せず、列挙したツールにそのターン限りの事前承認を与えるだけ(列挙外も通常の permissions に従って使える)と訂正し、本当に制限する手段(disallowed-tools / permissions.deny / subagent の tools)の比較表を追加。§3 の pipeline-trace 例のコードフェンス入れ子を修正。custom commands と skills の統合について冒頭に注記を追加。

この記事のタグ

Claude Code Harness

4

Claude Code をチームで運用するための設計。共有する .claude/ の構成から、hook によるガード、subagent と skill の使い分けまで。

  1. 1【設計】Claude Code Harness 入門 — チームで共有する .claude/ の3層構成(連載 第1回)
  2. 2【実装】Claude Code の hook で事故を未然に防ぐ — PreToolUse / PostToolUse ガードの実装パターン(連載 第2回)
  3. 3【設計】Claude Code の specialized agent をどう切り分けるか — メイン context を汚さない subagent 設計(連載 第3回)
  4. 4表示中【設計】Claude Code の skill と slash command の境界線 — 反復作業をどちらに寄せるか(連載 第4回)
この連載の一覧を見る