【Claude Code 連載 第90回】Agent Skills でエージェントを拡張する(agent-sdk/skills)

スポンサーリンク
【Claude Code 連載 第90回】Agent Skills でエージェントを拡張する(agent-sdk/skills) 用語解説
【Claude Code 連載 第90回】Agent Skills でエージェントを拡張する(agent-sdk/skills)
この記事は約33分で読めます。
よっしー
よっしー

こんにちは。よっしーです(^^)

スポンサーリンク

背景

この連載では、Claude Codeの公式ドキュメントを1ページずつ読み解いていきます。公式ドキュメントは情報が網羅されている分、「結局どの機能を、どんな場面で使えばいいのか」は自分で考える必要があり、読むのに意外と時間がかかります。そこで、私が実務で使うために読み込んだ内容を「使う場面→実例コード」の順に整理して残していくことにしました。専門家の解説というより、一次情報を読んだ記録です。推測や動作を確認していない部分には、その都度そう書きます。

公式ドキュメント:https://code.claude.com/docs/ja/agent-sdk/skills

1. 一言でいうと

スキルは、指示と説明(と必要なら補助のファイル)を SKILL.md というファイルにまとめた「専門の手順書」です。Agent SDK のエージェントは、ディスク上のスキルを自動で見つけ、関係のある場面で Claude が自分で使います。ユーザーが /<名前> と送って直接呼び出すこともできます。

このページはもう1つ、/compact や /clear のようなコマンドを SDK のセッションでどう扱うかも説明しています。スキルとコマンドは、どちらも「/名前 で呼び出せるもの」として同じ入口を共有しているからです。

第89回のサブエージェントとの大きな違いは、スキルはコードでは登録できないことです。サブエージェントは agents オプションでプログラムから定義できましたが、スキルはディスク上のファイルとして作るしかなく、SDK にスキルを登録する API はありません。


2. どういう場面で役立つか

シーン1:チームの手順をエージェントに覚えさせる

「セキュリティチェックではこの観点を見る」「PDF はこの手順で処理する」といった定型の手順をスキルにしてリポジトリに置いておくと、SDK のエージェントは関係する依頼が来たときに自分でそのスキルを使います。手順を毎回プロンプトに書かなくて済みます。

シーン2:使わせるスキルをエージェントごとに絞る

PDF と Word の処理だけを担当するエージェントには、skills オプションでその2つのスキルだけを渡します。関係のないスキルが Claude の候補に並ばないので、選び間違いが減ります。

シーン3:アプリから決まったコマンドを送る

長く続く会話の履歴を /compact で要約して軽くする、/security-check のような自作スキルをボタン1つで実行する、といった操作を、アプリからプロンプトとして送るだけで実現できます。

不要・向かないケース

  • スキルをコードの中で動的に作りたい:その API はありません。ファイルとして書き出す必要があります。実行時に振る舞いを変えたいなら、サブエージェント(第89回)やシステムプロンプト(第86回)のほうが向いています。
  • skills の許可リストで、ユーザーに特定のスキルを使わせないようにしたい:許可リストは Claude が呼ぶスキルを絞るだけで、ユーザーが /<名前> と送れば実行されます(後述)。
  • 1回きりの query() で /clear を使う:各呼び出しは最初から空の文脈で始まるので、効果がありません。

3. コードと仕組みの解説

原文の fenced コードブロックは23本です。21本を引用し、トラブルシューティングにある「作業ディレクトリを確認する」の対訳2本だけを散文にまとめます(3-1 のセットアップの例と同じ cwd・settingSources・skills の組み合わせで、cwd の値がパスの例になっているだけのため)。コードは原文から機械的に抜き出したもので、入れ子に由来する先頭インデントを外した以外は変えていません。

3-0. バージョン要件

挙動必要バージョン
どのコマンドにも一致しない /<名前> を、通常のメッセージとして Claude に送る(それ以前は Unknown command: /<name> を返して終わり)Claude Code v2.1.274 以降
skills リストの不正な名前を、セッション開始前に拒否するTypeScript Agent SDK 0.3.221 以降/Python Agent SDK 0.2.129 以降

3-1. SDK でのスキルの動き

Agent SDK でのスキルの扱いは次のとおりです。

性質内容
ファイルとして定義するスキルごとに専用のディレクトリを作り、その中に SKILL.md を置く(例:.claude/skills/<name>/SKILL.md)
ファイルシステムから読み込まれる読み込む場所は settingSources(Python は setting_sources)で決まる
自動で見つかる起動時にユーザーとプロジェクトのディレクトリからスキルのメタデータを見つけ、Claude がスキルを呼んだときに中身全体を読み込む
モデルが呼び出すClaude が文脈から、いつ使うかを自分で決める
ユーザーが呼び出すプロンプトで /<name> を送ると直接実行される(3-3)
skills オプションで範囲を決める見つかったスキルは既定で全部有効。スキル名のリスト、"all"、[] で、Claude が呼べるスキルを決める

「起動時はメタデータだけ、呼ばれたら中身」という読み込み方は、第70回で見たコンテキストの節約の仕組みと同じです。スキルがたくさんあっても、最初に載るのは名前と説明だけです。

スキルが見つかる場所。 既定の query() オプションでは、SDK はユーザーとプロジェクトの設定ソースを読み込むので、次の場所のスキルが使えます。

  • ~/.claude/skills/(個人用)
  • <cwd>/.claude/skills/(プロジェクト)
  • <cwd> の親ディレクトリからリポジトリのルートまでの各 .claude/skills/
  • additionalDirectories(Python は add_dirs)で渡した各ディレクトリの <dir>/.claude/skills/(SDK がそれらを Claude Code に --add-dir として渡すため。プロジェクトの設定ソースの扱い)

settingSources を明示的に指定する場合は、プロジェクトと追加ディレクトリのスキルを残すには 'project' を、個人用のスキルを残すには 'user' を含めます。特定のパスのスキルだけを読み込みたいなら、plugins オプションを使う方法もあります。

skills オプションで呼べるスキルを決める

query() の skills オプションで、そのセッションで Claude が呼べるスキルを決めます。

指定動き
省略見つかったスキルが有効になり、Skill ツールが使える。CLI と同じ動き
"all"見つかった全てのスキルを呼べる
スキル名のリスト挙げたスキルだけを呼べる
[]Claude はスキルを呼べない

たとえば2つのスキルだけを呼べるようにするには次のように書きます。

options = ClaudeAgentOptions(skills=["pdf", "docx"])
const options = { skills: ["pdf", "docx"] };

セッションでスキルを使えるようにする

skills を設定すると、SDK は Skill ツールを allowedTools に自動で加えます。ただし、tools で使えるツールの一覧を明示的に渡している場合は、その一覧に "Skill" を入れないと Claude はスキルを呼べません(第83回で見た「使える状態」の層の話です)。

次の例は、見つかった全てのスキルを有効にし、スキルがよく必要とするツールを事前承認します。cwd をプロセスの現在のディレクトリにしているので、現在のディレクトリか、リポジトリのルートまでの親ディレクトリに .claude/skills/ があるプロジェクトの中で実行します。

import asyncio
import os

from claude_agent_sdk import query, ClaudeAgentOptions


async def main():
    options = ClaudeAgentOptions(
        cwd=os.getcwd(),  # .claude/skills/ here or in a parent directory
        setting_sources=["user", "project"],  # Load skills from filesystem
        skills="all",  # Let Claude invoke every discovered skill
        allowed_tools=["Read", "Write", "Bash"],
    )

    async for message in query(
        prompt="Help me process this PDF document", options=options
    ):
        print(message)


asyncio.run(main())
import { query } from "@anthropic-ai/claude-agent-sdk";

for await (const message of query({
  prompt: "Help me process this PDF document",
  options: {
    cwd: process.cwd(), // .claude/skills/ here or in a parent directory
    settingSources: ["user", "project"], // Load skills from filesystem
    skills: "all", // Let Claude invoke every discovered skill
    allowedTools: ["Read", "Write", "Bash"]
  }
})) {
  console.log(message);
}

この例は Read・Write・Bash を事前承認しています。PDF の処理のようなスキルは、ファイルを読み書きしたりコマンドを実行したりするからです。Write と Bash が確認なしで動く点は意識しておく必要があります。スキルの中身が意図しない操作をさせる内容だった場合も、確認が出ません(筆者の指摘。3-6 も参照)。

スキルが読み込まれたことを確かめる

ストリームの最初のほうで、SDK はサブタイプ init のシステムメッセージを出します。その skills 配列を見れば、Claude が作業を始める前に、スキルが読み込まれたかを確かめられます。配列に載るのは、次の2種類です。

  • description か when_to_use の frontmatter フィールドを持つ、ユーザーが呼び出せるスキル
  • Claude Code に同梱されているスキル

注意点が2つあります。

  • frontmatter に user-invocable: false を書いたスキルは、配列に載りません。 読み込まれていて Claude は使えるのに、一覧には出ないので、「載っていない=読み込まれていない」とは限りません。
  • 配列の中身は skills のリストに関係なく同じです。 skills で絞っても、この一覧は絞られません。

特定のスキルだけを許す

特定のスキルだけを呼べるようにするには、その名前を skills のリストに入れます。名前は SKILL.md の name フィールドか、スキルのディレクトリ名と一致させます。プラグインが提供するスキルは plugin:skill の形で書きます(プラグインは第55〜61回)。

リストには正確なスキル名だけを書けます。ワイルドカードのように正確な名前として使えない書き方は、query() がセッションを始める前に拒否します(3-7)。全てのスキルを許したいときは、ワイルドカードではなく skills: "all" を使います。

リストに無いスキルについて、原文は次のように書いています。

  • モデルにはリストに無いスキルが見えず、Skill ツールもそれらを拒否する
  • しかし、スキルのファイルはディスク上に残り、Read や Bash で読める
  • リストで絞っても、名前での直接実行(/<名前>)は制限されない

つまり skills の許可リストは、Claude が自分から選ぶスキルを絞る仕組みであって、スキルを封じる仕組みではありません。たとえばチャットのアプリで、ユーザーの入力をそのままプロンプトとして渡していると、ユーザーが /<名前> と打てば、リストに無いスキルでも実行されます。複数の利用者がいるアプリでスキルを本当に使わせたくないなら、そのスキルを読み込まない設定(settingSources や作業ディレクトリの分け方)にするか、ユーザーの入力の先頭の / をアプリ側で扱う必要があります(原文の記述からの筆者の指摘)。

3-2. SDK セッションのコマンド

コマンドとは、プロンプトで /<name> と送って実行するものです。同じ入口に、出どころの違う4種類が並んでいます。

種類内容例
組み込みコマンドSDK が動かす Claude Code のプロセスに組み込まれた処理を実行する/compact
同梱スキルClaude Code に含まれているプロンプトの部品/code-review
自作のスキル自分で作るプロンプトの部品。ディレクトリごとに SKILL.md を持つ。ユーザーが呼び出せるスキルの名前は自動でこの入口に加わる/security-check
カスタムコマンドファイル同じ働きをする古い形式。.claude/commands/ に置く1枚の Markdown ファイルで、ファイル名がコマンド名になる。スキルが推奨の後継.claude/commands/deploy.md → /deploy

既定では、ユーザーも Claude も、どのスキルでも呼び出せます。どちらか一方からの呼び出しを、スキルの frontmatter で制限できます。

使えるコマンドを調べる

SDK を通じて、対話のターミナルが無くても動くコマンドを実行できます。system/init メッセージの slash_commands フィールドに、そのセッションで使えるコマンドが並びます。/theme や /terminal-setup のように対話のターミナルが必要なコマンドは載りません。

import { query } from "@anthropic-ai/claude-agent-sdk";

for await (const message of query({
  prompt: "Hello Claude",
  options: { maxTurns: 1 }
})) {
  if (message.type === "system" && message.subtype === "init") {
    console.log("Available commands:", message.slash_commands);
  }
}
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, SystemMessage


async def main():
    async for message in query(prompt="Hello Claude", options=ClaudeAgentOptions(max_turns=1)):
        if isinstance(message, SystemMessage) and message.subtype == "init":
            print("Available commands:", message.data["slash_commands"])


asyncio.run(main())

表示される一覧には、組み込みコマンド、同梱スキル、ユーザーが呼び出せるスキル、.claude/commands/ のファイルが混ざっています。

Available commands: ["clear", "compact", "context", "usage", "code-review", "verify", "security-check", ...]

user-invocable: false のスキルは、この一覧にも 3-1 の skills 配列にも出ません。MCP サーバーを設定しているセッションでは、MCP のプロンプトをコマンドとして使うこともできます。

言語の違いは、これまでと同じく init メッセージの読み方です。TypeScript は message.slash_commands、Python は message.data["slash_commands"] です。

名前でコマンドを実行する

コマンドは、プロンプトの文字列に入れて、普通の文章と同じように送ります。

  • 実行は skills オプションに左右されません。 /<name> を送れば、skills のリストに無いスキルでも、ユーザーが呼び出せるスキルなら実行されます。
  • 会話の履歴に作用するコマンド(/compact など)は、作用する対象の前のメッセージが必要です。

送った /<name> がどれにも一致しないとき、または使えないコマンドだったときの動きは次のとおりです。

状況動きモデルのターン
セッションのコマンドにも組み込みコマンドにも一致しない(v2.1.274 以降)失敗しない。プロンプトを通常のメッセージとして Claude に送り、「コマンドは実行されなかった」というメモを付ける。Claude の返事が返る使う
同上(v2.1.274 より前)Unknown command: /<name> を結果として返す使わない
このセッションでは使えない組み込みコマンド(/theme など)に一致する/theme isn't available in this environment. を結果として返す使わない

v2.1.274 以降は、打ち間違えたコマンドも Claude への問い合わせになり、そのぶんの費用と時間がかかります。ユーザーの入力をそのまま渡すアプリでは、/ で始まる入力を slash_commands の一覧と照らしてから送る、という工夫が考えられます(筆者の提案)。

コマンドも maxTurns の上限に達しうります。 他のプロンプトと同じく、上限に達すると success ではなくエラーの結果で問い合わせが終わります。上限に達するかもしれないコマンドは、try/catch(Python は try/except)でループを囲むか、作業が終わるのに十分な maxTurns を設定します。

/compact で履歴を要約する

/compact は、古いメッセージを要約しつつ大事な文脈を残して、会話の履歴を小さくします。要約するには、それなりの量の前のメッセージを持つ会話が必要です。次の例は、まず会話をしてから、それを要約し、結果を伝える compact_boundary システムメッセージを読みます。

import { query } from "@anthropic-ai/claude-agent-sdk";

// Compaction needs existing history, so have a conversation first
try {
  for await (const message of query({
    prompt: "Explain what this project does",
    options: { maxTurns: 2 }
  })) {
    if (message.type === "result" && message.subtype === "success") {
      console.log(message.result);
    }
  }
} catch (error) {
  // A single-shot query() throws after yielding an error result,
  // so the follow-up query below still runs.
  console.error(`Session ended with an error: ${error}`);
}

// Compact the same conversation
for await (const message of query({
  prompt: "/compact",
  options: { continue: true, maxTurns: 1 }
})) {
  if (message.type === "system" && message.subtype === "compact_boundary") {
    console.log("Compaction completed");
    console.log("Pre-compaction tokens:", message.compact_metadata.pre_tokens);
    console.log("Trigger:", message.compact_metadata.trigger);
    // Example output:
    // Compaction completed
    // Pre-compaction tokens: 1842
    // Trigger: manual
  }
}
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage, SystemMessage


async def main():
    # Compaction needs existing history, so have a conversation first
    try:
        async for message in query(
            prompt="Explain what this project does",
            options=ClaudeAgentOptions(max_turns=2),
        ):
            if isinstance(message, ResultMessage) and message.subtype == "success":
                print(message.result)
    except Exception as error:
        # A single-shot query() raises after yielding an error result,
        # so the follow-up query below still runs.
        print(f"Session ended with an error: {error}")

    # Compact the same conversation
    async for message in query(
        prompt="/compact",
        options=ClaudeAgentOptions(continue_conversation=True, max_turns=1),
    ):
        if isinstance(message, SystemMessage) and message.subtype == "compact_boundary":
            print("Compaction completed")
            print("Pre-compaction tokens:", message.data["compact_metadata"]["pre_tokens"])
            print("Trigger:", message.data["compact_metadata"]["trigger"])
            # Example output:
            # Compaction completed
            # Pre-compaction tokens: 1842
            # Trigger: manual


asyncio.run(main())

2回目の問い合わせは continue: true(Python は continue_conversation=True)で、1回目の会話の続きとして /compact を送っています(第87回の continue)。compact_boundary メッセージの compact_metadata に、要約前のトークン数(pre_tokens)ときっかけ(trigger。手で実行したので manual)が入っています。読み方は、TypeScript が message.compact_metadata、Python が message.data["compact_metadata"] です。

要約が行われないこともあります。 compact_boundary メッセージは、実際に要約したときだけ届きます。続きのセッションにメッセージはあっても要約できるものが無い場合、実行は success で終わりますが、compact_boundary は届かず、結果の文章に理由が入ります。たとえば、プロンプトはあるが Claude の返事がまだ無いセッションでは Not enough messages to compact. です。

新しく始める1回きりの query() は空の文脈で始まるので、このやり方は、前のターンがあるセッション(ストリーミング入力のモードや、セッションを再開したとき)で使います。

/clear で文脈をリセットする

/clear は会話を空の文脈に戻し、以後のプロンプトは前の会話の履歴なしで始まります。前の会話はディスク上に残るので、そのセッション ID を resume オプションに渡せば戻れます(第87回)。第68回で見た「/clear は破壊的ではない」と同じです。

/clear が役に立つのは、1つの接続で複数のプロンプトを送るストリーミング入力のモードです。1回きりの query() 呼び出しは、それぞれ最初から空の文脈で始まるので、/clear を送っても実際の効果はありません。代わりに新しい query() を始めます。

3-3. スキルを作る

スキルは、YAML の frontmatter と Markdown の本文からなる SKILL.md を入れたディレクトリとして作ります。description フィールドが、Claude がそのスキルを呼ぶタイミングを決めます。

.claude/skills/security-check/
└── SKILL.md

置き場所を選ぶ

よく使う置き場所は2つです。

置き場所パス使える範囲
プロジェクトのスキル.claude/skills/今のプロジェクトだけ
個人用のスキル~/.claude/skills/全てのプロジェクト

.claude/commands/ にある既存のカスタムコマンドファイルも、そのまま動きます。.claude/commands/deploy.md のコマンドファイルは /deploy になり、.claude/skills/deploy/SKILL.md のスキルと同じように働きます。コマンドファイルとスキルが同じ名前の場合にどちらが動くかは、CLI のスキルのページの「名前が重なるスキルの解決」に回されています。SDK は .claude/commands/ と ~/.claude/commands/ のファイルも、スキルと同じ2つの範囲から読み込みます。

最初のスキルを作って実行する

全体の流れを確かめるために、.claude/skills/security-check/SKILL.md を作ります。

---
name: security-check
description: Run a security vulnerability scan
---

Analyze the codebase for security vulnerabilities including:
- SQL injection risks
- XSS vulnerabilities
- Exposed credentials
- Insecure configurations

frontmatter に name と description(「セキュリティの脆弱性をスキャンする」)を書き、本文に、SQL インジェクション、XSS、露出した認証情報、安全でない設定、という調べる観点を並べています。

ファイルがあれば、スキルは SDK から使えます。Claude は依頼が説明に合うときに自分で呼び、ユーザーは直接実行できます。

import { query } from "@anthropic-ai/claude-agent-sdk";

for await (const message of query({
  prompt: "/security-check",
  options: { maxTurns: 10 }
})) {
  if (message.type === "result" && message.subtype === "success") {
    console.log(message.result);
  }
}
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage


async def main():
    async for message in query(
        prompt="/security-check", options=ClaudeAgentOptions(max_turns=10)
    ):
        if isinstance(message, ResultMessage) and message.subtype == "success":
            print(message.result)


asyncio.run(main())

成功すると、スキャンの結果を文章に含む success の結果で終わります。わざと問題を仕込んだ小さな Express のアプリに対して実行すると、結果の文章は次のように始まります。

**Security scan of `app.js` — 4 findings (most severe first):**

1. **SQL Injection** (line 8) — `req.query.name` is concatenated directly into the SQL string. Trivially exploitable (`' OR '1'='1`, `'; DROP TABLE users;--`). **Fix:** use parameterized queries, e.g. `db.query("SELECT * FROM users WHERE name = ?", [req.query.name], cb)`.
...

スキルの名前は、init メッセージの slash_commands 配列にも出てきます。

この例が maxTurns: 10 を指定しているのは、スキャンのためにファイルを何度も読むからです。3-2 で見たとおり、コマンドも maxTurns に達するとエラーで終わるので、作業量に見合った値にしておきます(例の値の意図は筆者の読み)。

同梱スキルと同じ名前に注意。 Claude Code には code-review と verify という同梱スキルがあります。.claude/commands/ にこれらと同じ名前のファイル(たとえば .claude/commands/code-review.md)を置くと、ファイルのコマンドが同梱スキルを覆い隠し、slash_commands にはその名前が1回だけ載ります。意図せず同梱スキルを使えなくしてしまわないよう、名前を確かめておく必要があります。なお第72回では同梱スキルとして /code-review・/batch・/debug を挙げましたが、今回のページは code-review と verify を挙げています。同梱スキルの一覧はバージョンで変わりうるので、手元では slash_commands で確かめるのが確実です(両回の記述の違いの指摘は筆者)。

3-4. スキルのためにツールを事前承認する

SDK のセッションでは、プロジェクトや個人のスキルが使うツールを、2つの方法で事前承認できます。

  • スキルの allowed-tools frontmatter
  • 問い合わせの allowedTools オプション(Python は allowed_tools)

ただし、組織が管理設定で allowManagedPermissionRulesOnly を有効にしている場合、Claude Code はこの両方を無視します。claude.ai から同期したスキルは、それ専用の frontmatter の規則に従います。

スキルはセッションのツールで動きます。 次の例は Read・Grep・Glob を事前承認しているので、Claude は 3-3 の security-check スキルを実行しながら、確認で止まらずにファイルを調べられます。

import asyncio

from claude_agent_sdk import query, ClaudeAgentOptions

options = ClaudeAgentOptions(
    setting_sources=["user", "project"],  # Load skills from filesystem
    skills="all",
    allowed_tools=["Read", "Grep", "Glob"],
)


async def main():
    async for message in query(prompt="Check this project for security issues", options=options):
        print(message)


asyncio.run(main())
import { query } from "@anthropic-ai/claude-agent-sdk";

for await (const message of query({
  prompt: "Check this project for security issues",
  options: {
    settingSources: ["user", "project"], // Load skills from filesystem
    skills: "all",
    allowedTools: ["Read", "Grep", "Glob"]
  }
})) {
  console.log(message);
}

ストリームの中では、スキルの呼び出しは Skill ツールの使用として現れ、その後にプロジェクトのファイルを読む Read の呼び出しが続きます。実行は、結果を文章に含む success で終わります。

このリストは名前を挙げたツールを事前承認するだけで、他のツールを制限するものではありません(第79・80回の「事前承認リスト」)。権限モードや canUseTool を含む権限の流れ全体は第80回の範囲です。

allowed-tools frontmatter の意味。 スキルの frontmatter でツールを事前承認できるということは、リポジトリに置かれたスキルが、そのリポジトリでエージェントを動かしたときの承認を前もって与えられるということです。第75回(claude -p が信頼していないフォルダのフックを動かす)や第84回(.mcp.json が既定で読み込まれる)と同じ構図で、他人のリポジトリで SDK のエージェントを動かすときは、プロジェクトの設定ソースを読み込むかどうかを意識して決める必要があります。組織の管理設定の allowManagedPermissionRulesOnly は、こうした事前承認をまとめて無効にする手段として読めます(第75・84回との対応づけは筆者)。

3-5. よくある問題

スキルが見つからない

設定ソースを確認する。 SDK は user と project の設定ソースを通じてスキルを見つけます。settingSources(Python は setting_sources)を明示的に指定し、これらのソースを外すと、SDK はスキルを読み込みません。

# Skills not loaded: setting_sources excludes user and project
options = ClaudeAgentOptions(setting_sources=[], skills="all")

# Skills loaded: user and project sources included
options = ClaudeAgentOptions(
    setting_sources=["user", "project"],
    skills="all",
)
// Skills not loaded: settingSources excludes user and project
const optionsWithoutSkills = {
  settingSources: [],
  skills: "all"
};

// Skills loaded: user and project sources included
const optionsWithSkills = {
  settingSources: ["user", "project"],
  skills: "all"
};

各ソースがどのディレクトリのスキルを読むかは、「Claude Code の機能」のページのファイルシステムのソースの表にあります。第79・86回で見た「settingSources: [] でファイルシステムの設定を全部切る」は、スキルも切ることになります。

作業ディレクトリを確認する。 SDK は、cwd オプションの .claude/skills/ と、そこからリポジトリのルートまでの全ての親ディレクトリからスキルを読み込みます。cwd が .claude/skills/ を持つディレクトリか、その下を指していること、同じリポジトリの中であることを確かめます。原文の対訳コードは、3-1 のセットアップの例の cwd を /path/to/project という固定のパスにしたもので、settingSources と skills の書き方は同じです。

ファイルの置き場所を確認する。

# Check project skills
ls .claude/skills/*/SKILL.md

# Check personal skills
ls ~/.claude/skills/*/SKILL.md

スキルが使われない

  • skills オプションを確認する。 skills のリストを渡しているなら、スキルの名前が入っているかを確かめます。Claude がリストに無いスキルを呼ぼうとすると、Skill ツールは Skill <name> is not in this session's skills allowlist を返します。名前をリストに足すか、プロンプトで /<name> を送って直接実行します(直接実行はリストに関係なく動きます)。
  • 説明を確認する。 具体的で関係のある言葉が入っているかを確かめます。効く説明の書き方は、Agent Skills のベストプラクティスにあります。第83・85回で見た「説明が呼び出しや検索の判断材料になる」は、スキルにも当てはまります。

スキル名が不正だというエラー

skills リストの名前が正確なスキル名として使えない場合、query() は Claude Code のプロセスを始める前にリストを拒否します。拒否されるのは次のような名前です。

  • 空の名前
  • 括弧、コンマ、制御文字を含む名前
  • 前後に空白がある名前
  • * だけや、末尾が :* のようなワイルドカードの形

エラーの出方は言語で違います。

言語出方例(["docs:*"] を渡したとき)空の名前のときチェックが入った版
TypeScriptError を投げる下の1本目Skill names must be non-empty strings.0.3.221 以降
PythonValueError を出す下の2本目Skill names must be non-empty strings0.2.129 以降
Invalid skill name "docs:*": wildcard-suffix names are not allowed; list each skill by its exact name.
ValueError: Invalid skill name 'docs:*': wildcard-suffix names are not allowed; list each skill by its exact name.

どちらも「ワイルドカードの末尾は使えない。各スキルを正確な名前で挙げること」と、破った規則を説明しています。それより前の版ではこのチェックが無く、不正な名前がそのまま通っていました。

YAML の書き間違いやデバッグなど、スキル全般の問題は CLI のスキルのページのトラブルシューティングに回されています。

3-6. 次に読むところ

スキルの書き方は CLI のスキルのページが詳しく、その内容は SDK のセッションにもそのまま当てはまります。原文は次の節を挙げています。

節内容
frontmatter のリファレンス使える全てのフィールド
スキルに引数を渡す$ARGUMENTS・$0・$1 とスキルの積み重ね。置き換えの全体表には名前付きの引数や ${CLAUDE_*} 変数もある
動的な文脈を差し込むClaude がスキルの中身を見る前に実行される !`command` の行
スキルを読み込む場所全ての置き場所、プラグインの名前空間、名前が重なったときにどちらが動くか

3つ目の「動的な文脈」は、スキルの中にコマンドの実行を書けることを意味します。スキルのファイルは手順書であると同時に、実行される内容を含みうるものです。3-4 の allowed-tools と合わせ、出どころの分からないスキルを読み込むときは、中身を確かめてからにするのが安全です(筆者の指摘)。


4. まとめ

  • スキルは SKILL.md にまとめた手順書。ディスク上のファイルとしてしか作れず、SDK に登録の API は無い(サブエージェントとの違い)。
  • 起動時に見つかるのはメタデータだけで、呼ばれたときに中身を読む。場所は ~/.claude/skills/、<cwd> からリポジトリのルートまでの .claude/skills/、追加ディレクトリの .claude/skills/。読み込みは settingSources(user/project)次第。
  • skills オプションは、省略=全部有効(CLI と同じ)、"all"、名前のリスト、[]。名前は正確に書き、ワイルドカードは使えない(不正な名前はセッション開始前にエラー)。tools を明示するなら "Skill" を入れる。
  • skills のリストは Claude が選ぶスキルを絞るだけ。リスト外のスキルもファイルは読めるし、/<名前> を送れば実行される。
  • コマンドの入口には、組み込みコマンド・同梱スキル・自作スキル・.claude/commands/ のファイルが並ぶ。使えるものは init の slash_commands で分かる(対話専用のものは載らない)。
  • どれにも一致しない /<名前> は、v2.1.274 以降は通常のメッセージとして Claude に送られ、ターンを使う。使えない組み込みコマンドはターンを使わずに断られる。コマンドも maxTurns に達しうる。
  • /compact は前の会話が必要(compact_boundary で結果が分かる。要約できなければ届かない)。/clear は前の会話をディスクに残したまま文脈を空にする。どちらも1回きりの query() では意味が無く、ストリーミング入力や再開したセッションで使う。
  • スキルのツールは allowed-tools frontmatter か allowedTools で事前承認できる(allowManagedPermissionRulesOnly では両方無視)。リポジトリのスキルが承認を持ち込めることに注意。
  • .claude/commands/ のファイルは同梱スキル(code-review・verify)を同名で覆い隠す。

次回予告

次回は 「ストリーミング入力と単一メッセージ入力」(agent-sdk/streaming-vs-single-mode) を取り上げる予定です。

今回、/compact や /clear は「1回きりの query() では意味が無く、ストリーミング入力のモードで役に立つ」と見ました。第81回でも、Claude の方向を丸ごと変えるにはストリーミング入力を使う、と案内されていました。次回は、1つの接続で複数のメッセージを送り続けるストリーミング入力と、1回ずつ問い合わせる単一メッセージ入力の違いと使い分けを見ていきます。


よっしー
よっしー

何か質問や相談があれば、コメントをお願いします。また、エンジニア案件の相談にも随時対応していますので、お気軽にお問い合わせください。

それでは、また明日お会いしましょう(^^)

コメント

タイトルとURLをコピーしました