ブログ一覧へ
活用ガイドLOG 175約20分で読める

Agent Skillsは別のAIエージェントへどこまでそのまま移せる? 同じSKILL.mdでも動きが変わる「相対パス・ツール名・権限・子エージェント」と、移す前の確認表

Agent Skillsは共通形式でも、移植先で同じに動くとは限りません。形式・発見・起動・パス・権限・子エージェント・成果を6段階に分け、Claude Code、Codex、Gemini CLI、Copilot、Cursorの公式資料を2026年10月23日に照合。Claude CodeとCodexで同じ教材を動かし、置き場所による発見の差と相対パスの挙動も実測しました。移す前に使える確認表つきです。

文・ニコ

2026年10月22日、Agent SkillsがAgentic AI Foundation(AAIF)に寄贈されました。発表では、2025年12月以来70以上のプラットフォームが統合したと説明されています。

じゃあ、Claude Codeで育てたSkillを、そのままCodexやCopilotに持っていっていいの?

ここ、気になりますよね。ぼくも気になったので、公式資料を読み比べて、手元のClaude CodeとCodexで実際に動かしてみました。

先に結論です。

  • 持ち運べるのは「手順と資料の入れ物」。 SKILL.mdと同梱のscriptや資料は、そのまま運べます。
  • 動きは移した先で別に確かめる。 どこで見つかるか、いつ呼ばれるか、何を実行できるかは製品ごとに違います。
  • 「出力が同じ」では、移せた証拠にならない。 今回の実測では、Skillが見つかっていないのに同じ答えが出る場面がありました。

この記事はAIエージェントを2つ以上使い分けたい人向けです。非エンジニアの人にも読めるよう、用語はそのつど説明します。最後に、コピーして使える確認表と、10分で試せる教材も付けました。

まず用語をそろえる

Agent Skillsはエージェントに「この仕事はこの手順でやってね」と教えるための、フォルダー1つ分の資料です。中心になるのはSKILL.mdというMarkdownファイル。先頭のYAML(設定を書く部分)に名前と説明を書きます。

skill-name/
├── SKILL.md       # 手順と、先頭に名前・説明
├── scripts/       # 必要なら、実行するコード
├── references/    # 必要なとき読む資料
└── assets/        # テンプレートやサンプル

この記事で言う「移植」は、このフォルダーを別のエージェントの環境に持ち込むことです。モデルの学習内容や、会話履歴、ログイン状態を移す話ではありません。

ちなみに、SKILL.mdだけをコピーしても、本文がscripts/やreferences/を頼りにしていれば、肝心な部分が抜けます。フォルダーごと運ぶのが基本です。Agent Skills形式仕様

「そのまま移せた」は6段階に分かれる

「移せた」と言っても、どこまで確かめたのかで意味が変わります。ぼくは次の6段階に分けて考えるのがいちばん安全だと思っています。これは調査をもとにした整理で、標準の認証制度ではありません。

移送・形式・発見・起動と参照・実行条件・成果の6段階。1〜2は共通仕様で確認でき、3〜6は移植先ごとに確認する
段階合格の証拠まだ分からないこと
1 移送本体と同梱物が揃い、ハッシュが一致製品が見つけるか
2 形式仕様と検証器を通る参照先や実ツールが本当にあるか
3 発見移植先の一覧に、正しい名前・説明・場所が出る必要なときに起動するか
4 起動・参照本体と必要な資料を読んだことがログで分かる依存コマンドや承認が満たされるか
5 実行条件コマンド、パス、権限、停止条件が意図どおり成果が正しいか、安定しているか
6 成果同じ入力で、独立した合格条件を満たす別の入力や、更新後でも同じか

1と2はファイルと仕様で確かめられます。3から先は 移植先で実際に動かさないと分かりません。 ここからは、この3〜6でつまずきやすい場所を順に見ていきます。

共通で決まっているのは、意外と少ない

共通仕様が決めているfrontmatter(先頭の設定欄)は6つだけです。

フィールド位置づけ移すときの注意
name必須。1〜64文字、小文字英数字とハイフン、親フォルダー名と一致共用するなら、a-z、0-9、-だけにする
description必須。1〜1024文字。何をして、いつ使うか自動で選ばれる手がかり。変えたら起動条件も見直す
license任意読めることと、再配布してよいことは別
compatibility任意。環境要件の説明依存を入れてくれる仕組みではない。説明欄
metadata任意。文字列のキーと値versionを書いても、版を解決する仕組みにはならない
allowed-tools任意。事前承認するツール。実験的ツール名や効き方が、全製品で一致する保証はない

出典:仕様のfrontmatter

ここに書かれていないことは、製品ごとに決められています。たとえば「どのフォルダーに置けば見つかるか」は、形式仕様の必須事項ではありません。実装者向けガイドでも、.agents/skills/は「相互運用に便利な慣例」として紹介されています。実装者向けガイド

つまずき1:置き場所。見つからないのに、答えは合う

公式資料の整理

2026年10月23日に読んだ公式資料では、最初に使う置き場所は次のとおりでした(全候補の一覧ではありません)。

実行面プロジェクト/個人の主な場所明示的な起動
Claude Code.claude/skills//~/.claude/skills//name
Codex(ローカルCLI・IDE).agents/skills/(作業場所からrepoルートまで)/~/.agents/skills/$name、/skills
Gemini CLI.gemini/skills/または.agents/skills/名前を指定して依頼。モデルがactivate_skillを呼ぶ
GitHub Copilot CLI.github/skills/、.agents/skills/、.claude/skills//name
Copilot in VS Code.github/skills/、.claude/skills/、.agents/skills//name
Cursor.agents/skills/、.cursor/skills/(Claude・Codex互換の場所も探索)/name

出典:Claude Code、Codex、Gemini CLI、Copilot CLI、VS Code、Cursor

見てのとおり、全員が読む共通の場所はありません。 CopilotとCursorは複数の置き場所を読んでくれますが、これは「片方向の優しさ」です。Copilotが.claude/skills/を読むからといって、Claude Codeが.agents/skills/を読むとは限りません。

実測:2か所に置いて、一覧に出るか見た

手元のClaude Code 2.1.308とCodex CLI 0.163.0で試すため、空のフォルダーを用意しました。そこに同じ教材(後述のportability-probe)を置きました。置き場所は.claude/skills/だけの場合と、.agents/skills/だけの場合で分けました。

Claude Codeは.claude/skillsだけ、Codexは.agents/skillsだけを一覧に出した。Claudeでは.agents側、Codexでは.claude側でも、モデルが直接読めば同じ答えが出る
  • Claude Code:.claude/skills/では、起動時の一覧にSkillが出ました。.agents/skills/では出ませんでした。
  • Codex:「いま渡されているSkill一覧にportability-probeはありますか。ファイル探索はしないで」と聞くと、.agents/skills/では説明文つきで「ある」、.claude/skills/では「ない」。どちらも2回ずつ同じ答えでした。

そして、ここからが本題です。

Claude Codeで教材を.agents/skills/に置いたときは、一覧に出ませんでした。それなのに、モデルがそのパスを直接読みにいって、正しい答えを返しました。 出力はちゃんと一覧に出た場合とまったく同じです。Codexも、.claude/skills/のときは別の道具でフォルダーを探索して、本体を読み、正解を出しました。

つまり、出力だけを見ると「移せた」ように見えます。でも、Skillとして発見されたわけではありません。モデルが頑張って探しただけです。この状態だと、次の依頼で探しにいくかは運しだいで、自動で選ばれる仕組みも働いていません。

もうひとつ。Codexで教材を.agents/skills/に置き、「コマンド実行なしでtextモードを」と頼んだ1回では、「ファイルを読むツールがない」と返して止まりました(BLOCKED)。Skillの本体を読むのにシェルが必要な環境で、「コマンドは使わない」と縛ると、Skill自体が読めなくなることがあります。1回だけの観察ですが、条件を決めるときには気をつけたいところです。

対策

  • 移植先の公式な場所に置き、移植先の一覧に出ることを確認する。
  • 一覧に出ないまま動いたときは、「発見された」ではなく「手動で読んだ」と記録する。
  • 同じ名前のSkillが複数の場所にあるときの優先順位も違います。Claude Codeはenterprise、personal、projectの順と説明し、Codexは同名のSkillが統合されず、両方が選択肢に出ることがあると説明しています。どちらも自分の環境の一覧と所在パスで確かめてください。Claude Codeの同名解決、Codexの同名Skill

つまずき2:相対パス。基準が2つある

SKILL.mdの中に[ルール](references/rules.md)と書くのと、python3 scripts/probe.pyと書くのは、似ているようで別物です。

Skillのルートを基準にする参照と、シェルの作業場所を基準にするコマンドは別の仕組み
  • 資料へのリンクは Skillのルート(SKILL.mdの親フォルダー)が基準。
  • シェルで実行するコマンドは いま居る場所(CWD) が基準。

個人のSkillをrepoの外に置いているとき、repoルートでpython3 scripts/probe.pyを実行すると、そこにある別のscriptを実行してしまうかもしれません。実装者向けガイドでも、スクリプトの呼び出しには絶対パスを使う例を示しています。Claude Codeは${CLAUDE_SKILL_DIR}という変数で解決しますが、これはClaude Codeの機能です。他の製品でも展開されるという決まりではありません。Claude Codeの文字列置換

実測:おとりを置いて、どちらが動くか

本当にそうなるのか、罠を仕掛けてみました。SKILL.mdには、あえて相対パスのまま「python3 scripts/probe.py --input assets/items.jsonを実行して」と書きます。作業フォルダー(CWD)には同じ名前のscripts/probe.pyを置きます。こちらは実行されると{"decoy":true}と出す「おとり」です。

Claude CodeとCodexで、各3回ずつ「scriptモードで実行して」と頼みました。

結果は、どちらも3回中3回、Skill自身のscriptが動きました。おとりは一度も動きませんでした。

  • Claude Codeは、Skillを呼んだ時の返答にBase directory for this skill: <絶対パス>が入り、モデルは毎回そのフォルダーにcdしてから実行しました。
  • CodexはプロジェクトのルートからSKILL.mdを読んだ後、相対パスのコマンドをSkillのフォルダーで実行しました。ただ、イベントログに作業フォルダーの指定が出ないので、どうやってそこで実行したかまでは確認できていません。

ほっとする結果ですが、ここから読み取りすぎないでください。試したのは各3回、この教材、この2製品、この時点の版だけです。「相対パスで書いても大丈夫」とは言えません。モデルがSkillの場所を知って気を利かせたら動く、というだけです。気を利かせない条件(長い会話の後、別のCWD、別のモデル、Windowsなど)は試していません。

対策(実践案)

共用するSKILL.mdには、基準を言葉で書いておくのが安全です。

今読み込んだSKILL.mdの親フォルダーをSとする。同梱ファイルはSから解決する。コマンドでは、scriptと入力の両方を絶対パスにする。Sが分からないときは、推測して実行せず、場所を確認する。

あわせて、~や個人の絶対パスを手順に埋め込まないこと、空白を含むパスを引用すること、Windowsの区切りやsandboxのマウント先も見ておくことです。

つまずき3:ツール名と権限。「書いた」と「許された」は別

allowed-toolsは、共通仕様では実験的なフィールドです。名前から「このSkillが使えるツールを制限する」と読みたくなりますが、少なくともClaude Codeでは違います。公式の説明を引用します。

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. [...] It does not restrict which tools are available: every tool remains callable, and your permission settings still govern tools that are not listed.

(allowed-toolsは、Skillを起動したターンの間、列挙したツールを承認なしで使えるようにする。[…] 使えるツールを制限するものではない。すべてのツールは呼び出し可能なままで、列挙されていないツールには権限設定が適用される)

出典:Claude Code Skills(2026年10月23日取得)

つまり、これは「事前承認」です。しかも次のメッセージを送ると解除されます。さらに、組織の管理設定でallowManagedPermissionRulesOnlyが設定されていると、プロジェクトや個人のSkillのallowed-toolsは無視されます(v2.1.282以降)。制限したいツールがあるなら、別のdisallowed-toolsや権限設定のdenyルールを使います。

製品ごとに見ると、同じ名前でも意味が違います。

実行面公式資料で確認できたこと残る確認
Claude Codeallowed-toolsはそのターンの事前承認。未掲載ツールを使えなくする機能ではないBashやReadの名前、パターン、denyや管理設定
Codex(ローカル)agents/openai.yamlにMCP依存を書ける。sandboxと承認はホスト側の設定今回のSkills資料では、allowed-toolsを強制のallowlistとして使える根拠は確認できない
Gemini CLIactivate_skillの承認で本体を受け取る起動への同意と、shell・ネットワーク・書込の許可は別。Policy Engineで確認
Copilot CLIfrontmatterのallowed-toolsによる自動承認の説明があるshell(...)などCopilot側の書き方。denyが優先される。Bash(...)を機械的に移せる根拠はない
Copilot in VS Code実行時のterminal承認設定を案内CLIの許可パターンが同じとは限らない
Cursor同梱scriptを、エージェントが使える形で実行するSkillsのfrontmatterだけでは、共通の権限付与は確認できない

出典:Claude Code、Codex Skills、Codexの承認とsandbox、Gemini CLI、Gemini Policy Engine、Copilot CLI reference、Copilotのツール許可、VS Code、Cursor

「対応不明」と「非対応」は別です。ここで「確認できない」と書いたのは、読んだ公式資料に記述が見つからなかったという意味で、ソースコードまで調べて非対応と証明したわけではありません。

同じCopilot系でも、SDKはさらに違います。SDK公式資料によると、SDKセッションではallowed-toolsで権限は与えられず、skill.invokedイベントで通知されるだけです。各ツール要求はpermission handlerが判断すると明記されています。CLIの説明をSDKに流用しないでください。Copilot SDK

「allowed-tools: Readと書いてあるから、このSkillは読み取り専用」と判断するのは危険です。disable-model-invocation: trueも、自動起動を止める設定であって、OSやAPIへの権限の壁ではありません。

書き方のコツ

共通部分の手順にはツール名ではなく 必要な能力を書きます。たとえば「対象文書を読み取る」「下書きを作る」といった具合です。そのうえで、環境別のメモに実際のツール名を対応づけます。さらに、「必要な能力が満たせないときは停止して理由を返す」まで決めておきます。「同等のツールを使って」の一言で、副作用や承認や範囲の違いを無視してはいけません。

つまずき4:子エージェント。親が知っていることは、子が知っているとは限らない

エージェントが仕事を分けて、別のエージェント(子エージェント)に任せることがあります。そのときは、ファイルの存在、使えるSkillの一覧、親がすでに読んだ本文、会話履歴、実行権限の5つを分けて考えます。親の頭の中がそのまま子に入るわけではありません。

実行面公式資料から確認できること読み足さないこと
Claude Codecontext: forkは、Skill本文を子へのpromptとして渡す。会話履歴を渡す機能とは別。子の定義のskillsは全文を事前に読み込ませる「子はSkillを使えない」も「親の会話や資料が全部渡る」も言えない
Codex(ローカル)子は親のSkills設定を引き継ぎ、skills.configで個別に無効化できる。sandboxと承認も親から継承親が読んだ本文や会話の全量が子で有効、とは言えない
Gemini CLI子は独立したcontext loopと固有のsystem promptを持つ読んだ資料には、Skillの継承の記述がない。未確認
Copilot CLIcustom agentsや委任の機能はある起動済みSkill本文が子へ自動で渡る範囲は、読んだ資料では確定できない
Copilot in VS Code実験的なcontext: forkは、専用の子contextでSkillを動かし、最終結果を親に返す任意の子の起動で、親のSkillや会話が継承される保証はない
Cursor子はクリーンな文脈で始まり、親が必要な情報をpromptに入れる親が読んだSkill本文が無条件に届くとは扱えない

出典:Claude Codeのfork、子へのpreload、Codex、Gemini CLI、Copilot CLI、VS Codeのfork、Cursor

Claude Codeでは手動専用(disable-model-invocation: true)のSkillを、子のskillsによる事前読み込みの対象にもできない、という例があります。ある設定が思わぬ別の経路に響く一例です。

なので、子に任せるときは「親を見て」で済ませず、渡す情報を書き出すのが安全です。

タスク:架空の作業一覧からopenのIDを抽出する。
Skill:移植先で解決したSKILL.mdの正確なパスと版(またはハッシュ)。
入力:下記JSON。親の過去の会話は前提にしない。
モード:text。コマンド、ネット接続、書込を使わない。
必読:Skill本体とreferences/rules.md。
出力:指定のJSON。読めない場合はBLOCKEDと不足物を報告。
合格条件:期待のID・件数・markerが一致し、許可外の操作がない。
報告の証拠:実際に読んだSkillのパスと参照パス、出力、停止理由。

これは親が渡す情報の案です。製品共通の子エージェントAPIではありません。子エージェントの実機挙動は、今回は試していません。

つまずき5:独自拡張。通るけど、機能は消える

Claude Codeのcontext: fork、$ARGUMENTS、${CLAUDE_SKILL_DIR}、Cursorのpaths、Codexのagents/openai.yaml。便利ですが、どれも共通仕様の6フィールドには含まれません。

項目共通で持ち運べる核製品固有・実行面固有
自動起動の抑制共通の統一フラグはないClaude Code・Cursor・Copilotはdisable-model-invocation。Codexはagents/openai.yamlのpolicy.allow_implicit_invocation
対象ファイルの絞り込みdescriptionや本文に書けるCursor・Claude Codeのpaths
引数・前処理本文に入力要件を書けるClaude Codeの$ARGUMENTSなど。他で展開される保証はない
子での実行本文を子に渡す設計は可能Claude Codeのcontext: fork。VS Codeにも実験的な同名機能
UIや接続の依存metadataに独自情報を入れられるCodexのagents/openai.yaml

出典:Claude Codeのfrontmatter、OpenAIの追加メタデータ、Copilot CLI、VS Code、Cursor

見落としやすい点が2つあります。

1つ目。未知のキーを黙って無視する製品と、拒否する経路がある。 Claude Codeは未知のfrontmatterを無視します。でも公式資料によると、claude.aiへのアップロード、Skills API、package_skill.pyでは、共通の6キーだけを受け付けます。独自キーを消せばアップロードは通っても、消したキーの機能まで移ったわけではありません。

2つ目。「Claude専用」と決めつけるのも不正確。 context: forkはVS Codeにも実験的に入っています。ただし、複数の製品が同じ拡張名を採用しても、共通仕様の必須機能にはなりません。

検証器が通っても、安心できない

公式リポジトリの参照検証器skills-ref(commit 69ef37e、パッケージ表記0.1.0)で、教材を少しずつ壊して試した結果が次のとおりです。調査担当のローカル確認で、ぼくは再実行していません。

条件結果言えること
完全な教材合格検査した形式に合う
nameを削除エラー必須項目は検出する
トップレベルにcontext: forkを追加エラー(Unexpected fields)製品の独自拡張は通さない
references/rules.mdだけ削除合格本文のリンク先の実在は、検査しない
allowed-tools: ImaginaryTool(存在しないツール)を追加合格ツールの実在や権限は、検査しない

検証器の合格は「形式の段階(2)」の確認にすぎません。リンク切れも、架空のツール名も通ります。参照元の公式リポジトリでも、skills-refはデモ用であって、本番SDKでも形式の正本でもないと説明しています。

研究では、何が分かっている?

「移植はうまくいく」と「うまくいかない」のどちらかを示す決定的な研究は、見つかりませんでした。代わりに、読む価値のある5本のプレプリント(査読前の論文)があります。数字は著者の報告で、ぼくが再現したものではありません。

  • 改変は実際に起きる。 公開forkから6つのSkillリポジトリ、1,126の改変事例を調べた研究があります。metadata、ホストやツール、手順や実行モデルの変更が観察されています(Wuほか、arXiv:2607.03238v1)。ただし、これは改変の履歴です。無変更で使われたSkillの成功率や、各変更の必要性を示すものではありません。改変の割合を、そのまま「移植の失敗率」にはできません。
  • 効果は条件で変わる。 SkillsBench(86タスク、評価は84タスク、Claude Code・Codex CLI・Gemini CLIの7構成、7,308軌跡)では、curated Skillsによる平均成功率の差は+16.2ポイントでした。悪化した例もあります(Liほか、arXiv:2602.12670v1)。一方、SWE-Skills-Bench(49のSkill、Claude Code+Claude Haiku 4.5の1構成)では、49のうち39のSkillで差が0、平均は89.8%から91.0%(+1.2ポイント)でした(Hanほか、arXiv:2603.15401v1)。対象も条件も違うので、並べて矛盾とは言えません。
  • 成功しても、手順の全体を試したことにはならない。 Skill CoverageはSkillの記述から検査できる制約を抜き出し、実行時にそれを試したかを見る指標です。v1では78タスク・7構成で39.90〜43.98%と報告されています(Tanほか、arXiv:2606.20659v1)。正常な1件だけでは、欠損や権限不足のケースが試されないまま残る、という示唆が得られます。
  • 安全性は形式への適合とは別。 改変したSkillと参照scriptを通じた有害操作の例が、Claude Codeなどで報告されています(Schmotzほか、arXiv:2510.26328v1、2025年当時の実験)。形式が正しい第三者のSkillを、信頼している業務コードと同じ権限で動かす根拠にはなりません。

原典:2607.03238v1、2602.12670v1、2603.15401v1、2606.20659v1、2510.26328v1

まとめると、5製品・6実行面で同じSkillを同じ条件で動かして同等性を示した研究は、見つかりませんでした。 この記事の確認表が必要な理由でもあります。

移す前の確認表

空欄は「合格」ではなく「未確認」として扱ってください。

確認すること具体的な方法足りないとき
出所と再配布条件作者・取得元・license・更新差分を読む利用条件を先に解決する
固定する版commit SHA、Skill内の版、全ファイルのハッシュを記録「最新」だけの記録にしない
パッケージの範囲script・reference・assetと、隠れた設定への依存を列挙本体だけのコピーを完成扱いしない
基本形式必須の名前・説明、親フォルダー名の一致、文字数、YAML元の製品の補完に頼る部分を直す
独自フィールド標準の6つ以外と、別の設定ファイルを抜き出す移植先の同等機能を確認。削ったときの機能差を記録
探索の場所移植先の公式な場所に置き、実際の一覧を見る手で読ませたなら「手動読取」と記録
同名の競合一覧の所在と版、pluginの名前空間を確認片方が隠れたまま評価しない
起動方法明示起動、自動選択、起動しない依頼を別々に試す説明・配置・設定・モデルを切り分ける
段階的読み込み必要な資料を読んだことを、ログで確認出力が似ているだけで「読んだ」としない
基準ディレクトリSkillの外のCWDから、同梱ファイルに届くかSkillのルートから絶対パスに解決
OSとシェル空白を含むパス、大文字小文字、区切り、実行権限対応するOSを明記
コマンドとライブラリ実行ファイルの場所と版、lockfile、最小の入力勝手にinstallして合わせず、不足を記録
ツール名とschema名前だけでなく、引数・戻り値・副作用を照合意味を保てる対応がなければ停止
認証と権限実行する主体・対象・操作・期限・範囲Skillに資格情報そのものを書かない
事前承認と禁止allowed-toolsの解釈、deny、sandbox、管理policy明示的な拒否を、起動の成功で上書きしない
ローカル/cloudmount、個人Skillの同期、network、入力の所在ローカルで読めたことを、cloudの成功にしない
子への引き継ぎ本体・入力・資料・ツール・権限を個別に点検「親を参照して」で省略しない
成功条件独立した正解、schema、必須の事実、許す副作用エージェントの自己申告だけで合格にしない
失敗条件不正な入力、欠けた資料、依存の欠如、承認の拒否BLOCKED・失敗・未実施を区別する
再評価の契機Skill・モデル・CLI・ツール・policyの変更を記録一度の成功を、更新後に引き継がない

cloudの例を1つ。Cursorは個人Skillのcloud同期を別機能として説明していて、同期の対象は~/.cursor/skills/だけです。~/.agents/skills/や、プロジェクト以外のローカル資料まで一緒に送られるとは説明していません。Cursorのcloud同期

OpenAI APIにも別の仕組みがあります。Responses APIのupload・version付きbundleや、Agents APIのsandbox内のcapability directoriesは、ローカルCodexの.agents/skills/の探索とは別のルールです。共通形式を使っても、ホストへの登録方法は違います。OpenAI APIのSkills

10分で試せる教材

確認表を見ただけでは、手が動きませんよね。そこで、害のない小さなSkillを用意しました。作業一覧のJSONから、openのIDだけを昇順で返します。

作るのは4ファイルです。実データも、認証も、外部送信も要りません。

portability-lab/
└── portability-probe/
    ├── SKILL.md
    ├── references/rules.md
    ├── assets/items.json
    └── scripts/probe.py

SKILL.md

---
name: portability-probe
description: Verify a harmless task-list portability probe. Use when asked to run portability-probe, compare skill portability, or list open fixture IDs. Do not use for production data.
compatibility: Text mode needs file reading only. Script mode needs Python 3.10+; no network or third-party packages.
metadata:
  author: research-example
  version: "1.0"
---

# Portability probe

1. Resolve this SKILL.md parent directory as the skill root S. Resolve bundled paths against S, not the shell working directory.
2. Read [the rules](references/rules.md) before processing data.
3. The default mode is text. In text mode, process the JSON supplied by the user without running commands. Return exactly one JSON object following the rules.
4. Only when the user explicitly requests script mode, check that Python 3.10+ is available. Read scripts/probe.py, then run it with --input set to assets/items.json. Resolve both paths to absolute paths under S. Return the script JSON unchanged.
5. Do not install software, use the network, read environment variables, write files, or edit the skill. If a required tool or file is unavailable, report BLOCKED with the missing requirement; do not substitute made-up results.
6. Never delegate this probe unless the user explicitly asks for a child-agent test. For that test, pass the exact skill path, input, requested mode, and output requirements to the child. Do not assume it received earlier conversation state.

references/rules.md

# Probe rules

Rule marker: probe-rule-v1

Input is a JSON array. Every item must be an object with a unique non-empty ASCII id matching [a-z][0-9]{2} and a status equal to open or done. An optional title is ignored. Reject invalid input: return {"error":"invalid-input"}. Otherwise keep only open items, sort their IDs in ascending ASCII order, and return {"ids":[...],"open_count":N,"rule_marker":"probe-rule-v1"}. Empty input returns an empty ID list and count zero. Do not invent or translate IDs.

assets/items.json

[{"id":"b02","title":"Beta","status":"open"},{"id":"c03","title":"Gamma","status":"done"},{"id":"a01","title":"Alpha","status":"open"}]

scripts/probe.py

import argparse
import json
import re
from pathlib import Path


def transform(items):
    if not isinstance(items, list):
        raise ValueError("invalid-input")
    seen = set()
    ids = []
    for item in items:
        if not isinstance(item, dict):
            raise ValueError("invalid-input")
        key = item.get("id")
        if not isinstance(key, str) or not re.fullmatch(r"[a-z][0-9]{2}", key):
            raise ValueError("invalid-input")
        if key in seen or item.get("status") not in ("open", "done"):
            raise ValueError("invalid-input")
        seen.add(key)
        if item["status"] == "open":
            ids.append(key)
    return {"ids": sorted(ids), "open_count": len(ids), "rule_marker": "probe-rule-v1"}


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("--input", required=True)
    args = parser.parse_args()
    try:
        items = json.loads(Path(args.input).read_text(encoding="utf-8"))
        result = transform(items)
    except (ValueError, TypeError):
        print(json.dumps({"error": "invalid-input"}, separators=(",", ":")))
        return 2
    except OSError:
        print(json.dumps({"error": "input-unavailable"}, separators=(",", ":")))
        return 3
    print(json.dumps(result, separators=(",", ":")))
    return 0


if __name__ == "__main__":
    raise SystemExit(main())

probe.pyのSHA-256は3d0d05042a1ba1fe4ae901f11dbc105b1bcaf1c9e60712bbc7b77efbe2b3115bです。コピーしたときに内容がずれていないか、確かめるのに使えます。

試し方

1. 空のrepoを用意し、移植先の公式の場所へ置く。 Claude Codeなら.claude/skills/portability-probe/、Codexなら.agents/skills/portability-probe/、Copilot CLIなら.github/skills/portability-probe/といった具合です。個人の常用Skillと混ぜないでください。

2. 一覧に出るか見る。 各製品の/メニューや/skillsで名前を確認します。一覧に出ないまま本文をチャットに貼って動いたら、それは「発見」ではなく「手動で与えた」試験として記録します。

3. textモードを試す。 製品の明示起動でSkillを選び、次のように頼みます。

portability-probeをtextモードで使ってください。
Skill本体とreferences/rules.mdを読み、次の入力を処理してください。
コマンド実行・書込・ネット接続・委任は使わないでください。
最終回答は規則どおりのJSONオブジェクトだけにしてください。

[{"id":"b02","status":"open"},
 {"id":"c03","status":"done"},
 {"id":"a01","status":"open"}]

期待する答えはこれです。キーの順番や空白ではなく、JSONとして比べます。

{"ids":["a01","b02"],"open_count":2,"rule_marker":"probe-rule-v1"}

プロンプトにmarkerの値は書いていません。ただし、markerが合っていても、「資料を読んだ」証明にはなりません。読取ログも見てください。

4. scriptモードを試す。 別の新しいセッションで、scriptモードで実行して。Skillの実体パスを基準に絶対パスを解決して。installや書込や委任はしないでと頼みます。期待する結果は同じJSONと終了コード0です。Skillの外の別フォルダーからも同じ結果になるか見てください。

5. 失敗の側も試す。

試験条件期待
空[]ids:[]、件数0
全件done[{"id":"a01","status":"done"}]ids:[]、件数0
重複ID同じIDのopenとdone{"error":"invalid-input"}
不正statuspaused同じエラー。勝手にopen扱いしない
不正IDA01同じエラー。勝手に小文字にしない
資料の欠損試験用コピーでreferences/rules.mdを外す不足を報告する。形式検査の結果と分けて記録
自動選択Skill名なしで「架空の作業一覧から、openのIDを規則どおり抽出して」起動したかを記録。生のJSON処理はできるので、出力だけでは起動の証明にならない
非該当「こんにちは」このSkillが起動しない
子への引き継ぎ明示的に子のtext処理を依頼子が読んだパス、入力、禁止操作の有無を、親とは別に記録

子や承認不足の試験は、その製品が提供する設定の範囲で行います。許可を広げて合格させないこと。削除や外部送信、本物の秘密を使う試験は要りません。

記録票

次の様式を条件ごとに1枚ずつ使います。初期値はNOT_RUNです。

host: "製品名と実行面(CLI/IDE/cloud等)"
host_version: "未記入"
model: "モデルID"
os_and_shell: "未記入"
skill_commit_or_hash: "未記入"
discovered_skill_path: "未記入"
invocation: "explicit / implicit / manual-file-read"
mode: "text / script / child-text"
permissions_and_sandbox: "未記入。秘密の値は書かない"
case_id: "normal"
discovery: "NOT_RUN"
activation: "NOT_RUN"
required_reference_read: "NOT_RUN"
execution: "NOT_RUN"
output_check: "NOT_RUN"
forbidden_side_effects_check: "NOT_RUN"
exit_code: null
output_artifact: "未記入"
trace_reference: "未記入"
stop_reason: "未記入"

判定はPASS、FAIL、BLOCKED、NOT_RUNに分けます。ログが見られないならUNOBSERVEDと書きます。ログが空だからといって、禁止操作がなかった証明にはなりません。

うまくいかないときの切り分け

観察先に調べること避ける判断
一覧に出ない場所、名前、YAML、重複、無効化、再読込、trust「モデルが弱い」
明示起動は動くが、自動で選ばれないdescription、対象範囲、一覧の省略、起動禁止の設定「形式が非互換」
本体は読んだが、参照を読まない参照を読む指示、同梱、パス「段階的読み込みだから必ず読んだ」
scriptが見つからないSkillのルートと作業場所、mount、引用、interpreter「SKILL.mdを書き直せば直る」
ツールが使えない公開されているツール一覧、schema、承認、認証の範囲「allowed-toolsに書けば用意される」
親では動くが、子では失敗Skillの可用性、事前読み込み、入力、資料、ツール、権限「親が知っているから子も知っている」
動いたが、成果が違う入力の版、手順の遵守、モデル、依存の版、正解の判定「形式検査に合格したから十分」

比べるときは、Skillの内容、入力、合格条件を固定し、独自設定の差を記録します。新しいセッションを3回ほど試す初期点検はよくありますが、3回は信頼性の保証ではありません。重要な業務に使うなら、リスクと許せる失敗率から、必要な回数を別に決めてください。

この記事で試したこと、試していないこと

手元で確かめたこと(2026年10月23日、macOS 27.0.1、Python 3.12.14)

  • Claude Code 2.1.308(モデルはclaude-opus-5-5)とCodex CLI 0.163.0(gpt-6-astra、推論effort high、read-only sandbox)で、上の教材を動かした。
  • 置き場所による発見の違い(各ホスト、2か所)。Codexの発見は同じ質問を2回ずつ、Claude Codeは起動時の一覧とモデルの動きを1回ずつ。
  • 相対パスのおとりの試験(各ホスト3回ずつ)。

試していないこと

  • Gemini CLI、Copilot CLI、VS Code、Cursorの実機(このMacにClaude CodeとCodexしか入っていないため)。表の行はすべて公式資料の読み取りです。
  • 子エージェントへの引き継ぎの実機、権限拒否の実機、cloud配置。
  • 製品間の成功率・費用・時間の比較。
  • 製品のバージョンが変わった後の挙動。

実験の回数は少なく、LLMの挙動にはばらつきがあります。「この環境で、この回数、こうだった」以上のことは言えません。教材とログの要約は、調べ方の記録として記事の素材フォルダーに置いてあります。場所はweb/public/blog/agent-skills-portability-checklist/experiment/です。

公式資料の一部には、更新日や必要な最小バージョンが書かれていません。表にまとめたのは、2026年10月23日に読んだ公開説明です。あなたの環境の版でも同じだと保証するものではありません。

まとめ

  • Agent Skillsで運べるのは、手順と資料の入れ物。発見・起動・実行条件・成果は移した先で確かめる。
  • 全員共通の置き場所はない。移植先の一覧に出ることを確認する。「出力が合う」は発見の証拠にならない。
  • 相対パスは、資料の参照とコマンドで基準が違う。コマンドには絶対パスを。
  • allowed-toolsは、少なくともClaude Codeでは「事前承認」で、制限ではない。製品ごとに意味を確認する。
  • 子エージェントには渡す情報を書き出す。
  • 検証器の合格は形式の確認だけ。

まずは、教材を1つの移植先に置いて、一覧に出るかを見るところから。それだけでも、「なんとなく動いた」が「ここまで確かめた」に変わります。

あなたの環境で試して、違う結果が出たら、それは大事な情報です。ぜひ記録票にして残してみてください。