0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

マルチエージェント対応のスキル配布、symlink で外出ししようとしたら踏んだ罠

0
Last updated at Posted at 2026-05-02

背景

Claude Code と Codex CLI を併用する中で、「片方で作ったスキルを、もう片方でも同じ感覚で使いたい」と思って、symlink で 1ソース化する構成を試していました。経緯はこちらに書いています。

Claudeで作ったスキルをCodexでも使えるようにした話 — スキルの二重管理をやめる

その記事で紹介した構成はこんな形でした。

リポジトリroot/
├── skills/                              # 本体 (agent-neutral, 1ソース)
│   └── git-sync-update-main/SKILL.md
├── plugins/                             # Claude Code 向け wrapper
│   └── git-sync/skills/update-main → ../../../skills/git-sync-update-main
└── codex/                               # Codex 向け wrapper
    └── skills/git-sync-update-main → ../../skills/git-sync-update-main

agent-neutral な skills/ を root に置いて、両 adapter から symlink で吸う。見た目もスッキリしてるし、新エージェントが増えても adapter を足すだけ。これでいけるはず、と push してマージしたんですが…

起きたこと: /plugin install で skill が認識されない

実際に Claude Code で動かしてみると、/plugin marketplace add t-tonton/tonton-ai-skills/plugin install git-sync@tonton-plugins何のエラーも出ない/reload-plugins してもロード数は変わるけど、肝心の /git-sync:update-main が呼べない。

何かがエラーも出さずに落ちている、と思って中を覗くと原因が見つかりました。

原因: Claude Code は cache 展開で symlink を辿らない

Claude Code は マーケットプレイス取得時プラグインインストール時 で、それぞれ別のディレクトリを使います。

  • マーケットプレイス取得 → ~/.claude/plugins/marketplaces/<marketplace>/ にリポジトリ全体を保存。symlink はそのまま保存される
  • プラグインインストール → ~/.claude/plugins/cache/<marketplace>/<plugin>/<version>/ にプラグイン本体だけをコピー。ここで symlink を辿らない

実際にローカルを覗くとこうなっていました。

~/.claude/plugins/marketplaces/tonton-plugins/plugins/git-sync/skills/
  update-main -> ../../../skills/git-sync-update-main   ← symlink (実体は marketplace 側 skills/ 配下に存在)

~/.claude/plugins/cache/tonton-plugins/git-sync/0.1.0/skills/   ← 空 dir

本体が plugin ディレクトリの (= リポジトリ root の skills/) にあると、cache 展開時にコピーされず、空 dir が残るだけ。Claude Code は cache を読みに行くので、skill が見つからないわけです。

公式ドキュメントの「プラグインキャッシングとファイル解決」にも明記されています。

プラグインがローカルマシンにクローンまたはコピーされると、~/.claude/plugins/cache のローカルバージョン管理プラグインキャッシュにコピーされます。これは、../shared-utils のようなパスを使用してプラグインディレクトリの外部のファイルを参照できないことを意味します。これらのファイルはコピーされないためです。

ドキュメントは「外部のファイル」と書いていますが、symlink でその外部を指している場合も同じ扱いになる、というのが今回踏んだ実態でした。

なぜ Codex 側は問題なかったのか

Codex CLI の skill 配置は ~/.agents/skills/ 配下を直接読みに行く方式で、cache を作りません。bash codex/install.sh でリポジトリ内の codex/skills/<flat-name>~/.agents/skills/<flat-name> に symlink で配置するだけ。

~/.agents/skills/git-sync-update-main
  → <repo>/codex/skills/git-sync-update-main
  → <repo>/skills/git-sync-update-main

OS レベルで symlink chain を解決できれば実体が見えるので、本体が plugin ディレクトリの外にあっても問題ない。

つまり、Claude Code と Codex で symlink との相性が違う というのが今回の核心でした。

直し方: source-of-truth の向きを反転

Claude Code 側の install を通すには、本体を plugin ディレクトリの に置く必要があります。なら、symlink の向きを反転します。

  • 本体を Claude Code が直接読むパス (plugins/<plugin>/skills/<skill>/SKILL.md) に 実ファイル として置く
  • Codex 側だけ symlink で吸う

修正後の構造はこうなりました。

リポジトリroot/
├── plugins/
│   └── git-sync/skills/update-main/SKILL.md       # ← 実ファイル (本体)
└── codex/
    └── skills/git-sync-update-main → ../../plugins/git-sync/skills/update-main

これで /plugin install が cache に本体を持っていけるようになり、Codex 側も symlink chain で本体に辿り着きます。トップ skills/ ディレクトリは廃止。二重管理ゼロは維持できました。

学び: マーケットプレイスを設計するとき

今回の罠から学んだのは:

  • SKILL.md 本体は plugin ディレクトリの内側に置くのが原則
  • 「外部に置いて symlink で配る」構造は /plugin install で詰む (silent に失敗するので気づきにくい)
  • どうしても外出ししたい場合は、CI / release 時に symlink を実体展開する tarball を作る等の工夫が要る

Codex 側のように cache を介さない仕組みなら symlink でも動くので、両エージェントを併用する場合は 本体を Claude Code の規約に揃え、symlink で吸われる側に Codex を回す のがいちばん素直です。

関連

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?