Max Nardit
← 記事
15 mindev

Claude Code のスラッシュコマンド: 知っておきたい組み込みコマンドと、自作の /command がいまはスキルである理由

commands フォルダはマクロではなく、Claude が呼び出せるコードとして扱ってください。どのファイルがモデルによる呼び出しを有効のままにしているかを監査し、スクリプト実行では終了コードを信用せずにターン数を確認し、副作用のある処理の保証は呼び出しフラグではなく権限とフックに置いてください。

目次

Claude Code のカスタムスラッシュコマンドの旧ページは、いまは別のドキュメントを返します。code.claude.com/docs/en/slash-commands を取得すると「Extend Claude with skills」が返ってきます。スキルのページとバイト単位で同じファイルで、冒頭近くに 1 行、「Custom commands have been merged into skills.」(カスタムコマンドはスキルに統合されました)とあります。.claude/commands/ フォルダはいまも動きます。changelog によれば、この統合で挙動は変わっていません。逆から読むと、このフォルダのファイルは以前から、そしていまも、オプトアウトしない限り Claude が自分の判断で実行できるものだということになります。さらに、シェルコマンドを差し込むスキルは Claude が読む前に失敗することがあり、そのとき claude -p は 0 で終了して、その実行を成功と報告します。

どちらも使い捨てのリポジトリで確認しました。Linux 上の Claude Code 2.1.319 と git 2.43.0 で、すべて claude -p と Haiku による非対話実行です。実行結果は出力付きで示します。ドキュメントだけを根拠にする主張はそう明記し、その両方を超えて推論している箇所にもその旨を記します。

Claude Code の組み込みコマンドで知っておくべきものはどれですか?

セッションそのものを管理するコマンドです。スキルでは代わりがきかないからです。コマンドリファレンス には 3 種類、100 を超える項目が並んでいます。大半は組み込みコマンドで、CLI にコードとして組み込まれた固定のロジックです。いくつかは Workflow と表示されています。たとえば /deep-research は作業をサブエージェントに分散します。20 個は Skill と表示されています。/code-review、/doctor、/debug、/loop、/simplify のような同梱スキルで、ドキュメントによれば「a prompt handed to Claude」(Claude に渡されるプロンプト)、つまり自分で書くスキルと同じ仕組みです。

Anthropic 自身によるセッションの流れの解説は、妥当な候補リストになります。リポジトリのセットアップには /init、/memory、/permissions、/mcp。作業中は /plan、/model と /effort、ウィンドウを何が埋めているかを見る /context とそれを縮める /compact、履歴に残らない脇道の質問用の /btw。出荷前には、作業ツリーを見る /diff、現在の diff を見る /code-review、自分のブランチを origin のデフォルトブランチと比べる /security-review(このリモートが必要です)。何かが壊れたら、チェックポイントに戻る /rewind、セットアップを点検する /doctor。同じリファレンスによれば、自分でスキルを書くようになると、さらに 3 つが重要になります。/skills は読み込まれているものを一覧し、/reload-skills はセッション途中で作ったスキルフォルダを拾い、/skill-doctor は各スキルがコンテキストをどれだけ使い、どれくらいの頻度で実行されるかを報告します。

ドキュメントにある 2 つのルールで、入力の仕方が変わります。どのコマンドも、メッセージの先頭にあるときだけ実行されます。スキルにはその続きがあります。「じゃあ staging に /deploy しておいて」のように普通の文の後に書くと、その名前は直接は何も実行しませんが、回答中に Claude がそのスキルを実行してよいという許可として扱われます。また、組み込みコマンドと同じ名前で書いたスキルは、ローカルのターミナルではその組み込みコマンドを置き換えます。ただしエイリアスは置き換えません。

Claude Code のカスタムコマンドはいまはスキルなのですか?

はい。フォルダ構成以外はすべての点でそうです。ドキュメントによれば、.claude/commands/deploy.md と .claude/skills/deploy/SKILL.md は「both create /deploy and work the same way」(どちらも /deploy を作り、同じように動く)とあります。コマンドファイルは、name と paths を除いてスキルと同じフロントマターを受け付けます。スキルフォルダが加えるのは補助ファイルを置く場所です。Claude が必要なときだけ読むリファレンス文書や、読み込まずに実行するスクリプトです。

同じ名前で両方あるときは、スキルが優先されます。zt-hello という名前のコマンドファイルとスキルを並べて置き、コマンドには COMMAND、スキルには SKILL と返すよう指示しました。

shell
$ claude -p "/zt-hello" --model haiku
SKILL

.claude/commands/ のサブフォルダは、いまもコロンで名前空間を作ります。.claude/commands/zt-ns/zt-comp.md にあるファイルは /zt-ns:zt-comp で応答しました。スキルフォルダはこのようには入れ子にならず、移行のときにこれが効いてきます(後述)。

Claude Code の古い .claude/commands ファイルを Claude は自分の判断で実行できますか?

はい。しかも新しい話ではありません。1.0.123 の changelog エントリは SlashCommand ツールを追加しており、これは「which enables Claude to invoke your slash commands」(Claude があなたのスラッシュコマンドを呼び出せるようにする)ものでした。コマンドをスキルに統合した 2.1.3 のエントリは、それが「with no change in behavior」(挙動の変更なし)だったと書いています。統合で変わったのは、読む側が目にする説明です。コマンドファイルはいまはスキルとして説明されており、スキルの description は Claude のコンテキストに置かれ、Claude はそれを見ていつ読み込むかを判断します。フォルダをマクロ集だと考えていると、ここは読み飛ばしがちです。Claude に、呼び出せる zt- スキルを一覧させました。

shell
$ claude -p "List the names of every skill you can invoke with the Skill tool whose name starts with zt-. Names only." --model haiku
zt-hello
zt-ship
zt-ns:zt-comp

zt-ns:zt-comp がコマンドファイルで、フロントマターはまったくありません。ドキュメントによれば、description がない場合は本文の最初の空でない行が使われるので、プロンプトだけのファイルでも一覧に載ります。Skill ツール経由で呼ぶよう指示すると、Claude は Launching skill: zt-ns:zt-comp を受け取り、NESTED と答えました。一覧に出なかった 4 つ、zt-args、zt-diff、zt-diffsafe、zt-deploy は、disable-model-invocation: true を設定しているものです。

つまり、手で起動するマクロとして書いたコマンドファイルは、会話の流れが合いそうだと Claude が判断したときに実行しうるものでもあります。/summarize なら害はありません。デプロイ、送信、削除をするファイルでは、想定していなかったかもしれない挙動であり、ファイル自体にはそれを示すものが何もありません。

Claude Code で引数を取るスキルはどう書きますか?

SKILL.md を置いたフォルダを作り、--- 行で挟んだ YAML フロントマターと、Markdown で書いた指示を入れます。コマンド名の後ろにあるものはすべて $ARGUMENTS として届きます。$0、$1 などは、シェル風のクォートでそこからインデックス指定します。テスト用のスキルです。

markdown
---
description: Echo arguments test
disable-model-invocation: true
---
Reply with exactly this line and nothing else: FIRST=[$0] SECOND=[$1] THIRD=[$2] ALL=[$ARGUMENTS]
shell
$ claude -p '/zt-args "hello world" second' --model haiku
FIRST=[hello world] SECOND=[second] THIRD=[$2] ALL=["hello world" second]

クォートした 2 語は 1 つの引数のまま残り、その位置に何もない $2 は、ドキュメントのとおりリテラルの文字列として残りました。ドル記号を含む文章を書く前に、ドキュメントからあと 3 点です。

  • 引数を受け取るプレースホルダーがない場合、Claude Code は引数を ARGUMENTS: 行として末尾に追加するので、Claude は入力内容を見られます。
  • 名前付き引数はフロントマターの arguments リストから作られます(arguments: [issue, branch] なら $issue が 1 つ目になります)。何も渡されなかった名前付きプレースホルダーは、上の $2 とは違って空文字列に展開されます。
  • 本文に $1.00 のような価格をそのまま書くときは、バックスラッシュを 1 つ付けて \$1.00 とします。そうしないと、2 つ目の引数があるとき、その $1 が 2 つ目の引数に展開されます。

ほぼすべてのスキルで設定する 2 つのフィールドは、description(スキルが何をするか、いつ使うか。description と when_to_use の合計は一覧で 1,536 文字で切られるので、主な用途を先に書きます)と、オートコンプリート中に表示されるプレースホルダーの argument-hint です。スクリプトを実行するスキルでは、2 つの変数を使う価値があります。セッションがどのディレクトリにいても同じように解決されるからです。

${CLAUDE_SKILL_DIR}    the folder that holds this SKILL.md
${CLAUDE_PROJECT_DIR}  the project root

Claude Code の disable-model-invocation は実際に何を止めるのですか?

Claude が自分の判断で Skill ツール経由でスキルを読み込むことを止めます。しかもモデルに行儀よくするよう頼むのではなく、Claude Code 自身が強制します。このフラグを設定すると、description は Claude のコンテキストから完全に消えます。上の一覧に zt-deploy がなかったのはそのためです。それでも呼び出すよう Claude に指示すると、Skill ツールは次の内容を返しました(実行時の stream-json ログから)。

<tool_use_error>Skill zt-deploy cannot be used with Skill tool due to
disable-model-invocation. Ask the user to run /zt-deploy themselves — it
cannot be invoked via the Skill tool. Do not replicate this skill's workflow
by other means — it is reserved for explicit user invocation.</tool_use_error>

ブロック自体は本物です。モデルが何を望んでも Skill ツールは拒否します。最後の文は別種のものです。「Do not replicate this skill's workflow by other means」(このスキルのワークフローを他の手段で再現しないこと)はモデルが読むテキストであって、ハーネスが実行するチェックではありません。そして 文は重み付けされるだけで、強制はされません。このフラグは Skill ツールの経路を塞ぎます。スキルの手順が使うはずのシェル、ファイル、ネットワークを取り上げるわけではありません。さらに、その経路でも絶対ではありません。ドキュメントによれば、自分のメッセージで普通の文の後に /zt-deploy と書くと許可として扱われ、2.1.110 以降はこのフラグを持つスキルでもそれが効きます。

もう一方のフィールドは逆の役割を持ち、名前が似ているので混同しやすくなっています。

フロントマター/name と入力できるClaude が呼び出せるClaude のコンテキストに description がある
(デフォルト)はいはいはい
disable-model-invocation: trueはい自分の判断では不可いいえ
user-invocable: falseいいえはいはい

user-invocable: false は、レガシーシステムの仕組みのような背景知識のためのものです。Claude が適用すべきだけれど、誰もコマンドとしては必要としない知識です。スキルを / メニューから隠し、ドキュメントによれば、名前を入力しても無視します。Claude を止める効果はまったくありません。

フロントマターのフラグだけがスイッチではありません。ドキュメントによれば、skillOverrides のエントリでファイルを編集せずに同じことができ、共有リポジトリにチェックインされたスキルにはこの方法を使います。/skills メニューはこれを .claude/settings.local.json、つまりそのプロジェクト用のコミットされない自分の設定に書き込みます。

json
{
  "skillOverrides": { "deploy": "user-invocable-only" }
}

代わりに "off" にすると、スキルは Claude からも / メニューからも隠れ、フルネームを入力しても実行されずにエラーが返ります。別のレバーとして、権限設定に Skill(deploy *) のような deny ルールを置くと、フロントマターの内容にかかわらず、そのスキルは Skill ツールから外れます。

Claude Code スキルの allowed-tools は何かを制限しますか?

いいえ。事前承認するだけです。ドキュメントは明確です。allowed-tools は、「during the turn that invokes this skill」(このスキルを呼び出したターンの間)、列挙したツールを確認なしで Claude に使わせます。この許可は次のメッセージを送ると消え、「it does not restrict which tools are available」(利用できるツールを制限するものではない)とあります。allowed-tools: Bash(git push *) を持つデプロイ用スキルは、Claude を git push に閉じ込めません。1 ターンの間、git push の確認を省くだけです。

ツールを外すフィールドは disallowed-tools で、これもスキルが有効な間だけ続きます。恒久的な制限は権限ルールに置きます。deny ルールと ask ルールは、スキルの allowed-tools よりも優先されます。

同じページからもう 1 つの性質です。「Workspace trust doesn't gate this field.」(ワークスペースの信頼はこのフィールドを制御しない)。プロジェクトスキルの allowed-tools は、一度も信頼していないフォルダでの claude -p 実行でも適用されます。これは クローンしたリポジトリの設定がまだできること の一覧に入れるべき項目です。リポジトリで Claude Code を実行する前に、その中のスキルのフロントマターを読んでください。

副作用のあるものについて、各層を私はこう読んでいます。ドキュメントはそれぞれを説明していますが、順位は付けていません。disable-model-invocation は、誰がスキルを開始してよいかを決めます。権限ルールは、誰が開始したかにかかわらず、どのコマンドがそもそも実行できるかを決めます。フック は、Claude がスキルに従っているかどうかに関係なく、イベントが発生するたびに実行されます。だからこそ、「Claude skipped a rule that must hold every time」(毎回守られるべきルールを Claude が飛ばした)場合のドキュメント自身の対処は、そのルールをフックに移すことになっています。1 つ目は意図のために、2 つ目と 3 つ目は保証のために使ってください。

Claude Code スキルのシェルコマンドが失敗しても claude -p が 0 で終了するのはなぜですか?

Claude が 1 ターンも実行する前にスキルが中断され、ヘッドレス実行の中でそれをエラーとして数えるものが何もないからです。ドキュメント自身の最初の例でこれが起きます。コミットのないリポジトリでは git diff HEAD が失敗し、差し込みコマンドが失敗するとスキル全体が中断されます。その例は !`git diff HEAD` で始まります。これは Claude が何かを見る前にコマンドを実行し、その出力をプロンプトに差し込む記法です。初期化したばかりのリポジトリでは次のようになります。

shell
$ claude -p "/zt-diff" --model haiku
Shell command failed for pattern "!`git diff HEAD`": [stderr]
fatal: ambiguous argument 'HEAD': unknown revision or path not in the working tree.
Use '--' to separate paths from revisions, like this:
'git <command> [<revision>...] -- [<file>...]'

ここまではドキュメントに書いてあります。コマンドが失敗すると呼び出しは中断され、Claude はスキルを見ません。(デフォルトのシェルでは 0 以外の終了コードはすべて失敗として扱われます。ただし grep のような検索・比較コマンドの終了コード 1 は除きます。)ドキュメントが触れていないのは、スクリプト実行でそれがどう報告されるかです。

shell
$ claude -p "/zt-diff" --model haiku < /dev/null > /dev/null; echo "exit=$?"
exit=0
 
$ claude -p "/zt-diff" --model haiku --output-format json
{"is_error": false, "subtype": "success", "num_turns": 0, "result": "Shell command failed for pattern ..."}

JSON は重要な 4 つのフィールドに絞ってあります。終了コードは 0、is_error は false、subtype は success です。どちらかを確認する cron ジョブや CI ステップは、実行されなかったスキルを成功として記録します。見分ける手がかりは num_turns: 0 と、「Shell command failed for pattern」で始まる result です。スクリプトからスキルを呼ぶなら、これらを確認してください。

スキル側の修正は、0 以外で終了することが想定されるコマンドについてドキュメントが示しているものです。末尾に || true を付けます。こうすると git のエラーテキストはそのままプロンプトに入り(デフォルトのシェルでは Claude Code が stderr を差し込み出力にまとめるので、下の 2>&1 は明示しているだけです)、Claude は diff が空である理由を見られます。

markdown
## Current changes
!`git diff HEAD 2>&1 || true`

その行を入れた同じスキルは、読み込まれて普通に回答しました。ドキュメントにある関連ルールを 1 つ挙げます。auto モード以外では、権限ルールで許可されていない差し込みコマンドも、確認を求めずにスキルを中断させます。allowed-tools で事前承認しておいてください。

Claude Code で .claude/commands のファイルをスキルに移すにはどうしますか?

期限はありません。コマンドファイルは動き続けます。コマンドに補助ファイルが必要になったら、そのファイルを移してください。すべてのファイルの確認はいま済ませてください。理由は上で述べた呼び出しのデフォルトです。うまくいく手順は次のとおりです。

  1. Claude が呼び出せるコマンドファイルを見つける。 フロントマターでフラグを有効にしていないものすべてです。ファイル全体に対する単純な grep は本文中の例にだまされ、ドキュメントが受け付ける yes や True を見逃します。そこで、次のコマンドはフロントマターのブロックだけを読みます。

    shell
    find .claude/commands ~/.claude/commands -name '*.md' 2>/dev/null | while read -r f; do
      awk 'NR==1 && $0!="---" {exit} NR>1 && $0=="---" {exit}
           NR>1 && tolower($0) ~ /^disable-model-invocation: *(true|yes|on|1) *$/ {found=1}
           END {exit !found}' "$f" || echo "model-invocable: $f"
    done

    テストリポジトリでは、フラグを設定していない 3 つのコマンドファイルが一覧に出ました。本文でフラグに触れているだけのファイルも含まれ、Yes に設定したファイルはスキップされました。これはテキストのチェックです。同じ名前でファイルを上書きするスキルや、skillOverrides のエントリは見えません。この記事のために自分の個人スキルフォルダに同じ grep をかけたところ、フラグを設定しているスキルは 1 つもありませんでした。それが正しいかどうかは、各スキルが何をするかによります。このチェックは問いを目の前に置くだけです。

  2. 副作用のあるものすべてにフラグを付ける。 ファイルを移すかどうかに関係なく付けます。

  3. フォルダが必要なものを移す。 mkdir -p .claude/skills/deploy を実行し、追跡済みのファイルなら git mv .claude/commands/deploy.md .claude/skills/deploy/SKILL.md、Git が追跡していないファイルなら普通の mv を使います。コピーではなく移動することは見た目ほど重要ではありません。どのみち名前の衝突ではスキルが優先されるからです。ただ、同じ名前のファイルが 2 つあると、後で混乱することになります。

  4. 名前空間付きのコマンドは意図して名前を変える。 .claude/commands/frontend/component.md は /frontend:component です。スキルはコマンド名をフォルダ名か name フィールドから取るので、移した版には frontend-component のような新しい名前を自分で付けることになります。古い名前が手に染みついている人には知らせる必要があります。

  5. フロントマターを検証する。 YAML がパースできないファイルも読み込まれますが、そのときはすべてのフィールドが黙って捨てられます。いま追加したフラグも例外ではありません。バリデーターは .claude/skills と .claude/commands の両方のフォルダに対して実行し、単一のスキルフォルダには決して実行しないでください(プラグインとして扱われ、マニフェストがないため失敗します)。

    shell
    $ claude plugin validate .claude/skills
    Validating skill: .claude/skills/zt-broken/SKILL.md
     
    ✘ Found 1 error:
      ❯ frontmatter: YAML frontmatter failed to parse: YAML Parse error: Unexpected character. At runtime this skill loads with empty metadata (all frontmatter fields silently dropped).

    これは意図的に壊したテスト用スキルでの出力で、パスは短縮してあります。それを除いた同じフォルダでは、✔ Validation passed と表示されました。.claude/commands に対しては、フロントマターブロックのないコマンドファイルについての警告も出て、警告付きで合格します。

.claude/commands/ にあるファイルはマクロ、つまり自分で実行するものに見えます。スキルとしては、自分と Claude の両方が実行できるものであり、両方がデフォルトです。たいていはそのデフォルトが望ましいものです。それがスキルの目的だからです。見直す価値があるのは、マクロとして書かれたファイル、つまり実行するのは自分だけだという前提で書かれたファイルです。

01 / ディスカッション

コメント欄はありません。議論は X で行っています。

Max Nardit@mnardit

02 / ほかの記事

  1. Claude Code と Gemini CLI の分業:開発者による 7 つの使い分けと、6 月 18 日を越えて残ったもの

    2 つのツールを組み合わせる 2025 年のセットアップは、無料の Google ログインと、より大きなコンテキストウィンドウに頼っていました。そのログインは 6 月に別のツールへ移り、ウィンドウでは Claude Code が追いつきました。どのセットアップが残ったかを決めたのは、ツールそのものではなく、各呼び出しの背後にあるアカウントでした。

  2. Claude Code のバックグラウンドサブエージェントと利用上限:ステータスラインが99%安くなるという Anthropic の主張と、自分で測れるキャッシュ書き込み

    サブエージェントの要は分離にあり、自分のトランスクリプトでは、そのコストの大半が5分のキャッシュ書き込みでした。Max プランで計測すると、バックグラウンドサブエージェントは価格換算した使用量の約11.5%で、その64%がキャッシュ書き込みです。再現用の Python スクリプトと、4つのレバーがそれぞれコストのどの部分に効くかをまとめます。

  3. Claude Code の worktree: claude --worktree が分離するものと、ポート、データベース、インストールのように分離しないもの

    Claude Code 2.1.316 で検証しました。2 つの worktree が同じポートで衝突し、Docker Compose はそれぞれに別の Postgres ボリュームを計画し、スクリプト実行はロックされたチェックアウトと不要なブランチを残し、デフォルトのベースは未プッシュのコミットを含みませんでした。