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?

Codexのプラグインマーケットプレイス、同名衝突は優先順位ではなくエラーで止まった

0
Posted at

TL;DR

  • Codex CLI 0.147.0 のプラグインマーケットプレイスを、ローカルディレクトリ 2 つで実際に構成して挙動を確かめました
  • リポジトリに .agents/plugins/marketplace.json を置いただけでは認識されません。codex plugin marketplace add の明示登録が要ります
  • 登録先は ~/.codex/config.toml で、カレントディレクトリに関係なく全ディレクトリから見えます。リポジトリ単位の隔離にはなっていません
  • マニフェストの "path": "./plugins/xxx"マーケットプレイスのルート基準 です。marketplace.json のあるディレクトリ基準ではありません
  • 同名プラグインが 2 つのマーケットプレイスにあるとき、Codex は優先順位で勝者を決めず <plugin>@<marketplace> の明示を要求してエラーで止まります

はじめに

対象読者は、Codex CLI で自作プラグインをチームに配りたい、あるいは配られたプラグインを取り込む側の開発者です。

Codex の Agent Plugins は段階的に入りました。公式チェンジログによると、2026 年 7 月 29 日の 0.146.0 で「Agent Plugins のマニフェスト、ワークスペースへのプラグイン公開、追加のプラグインマーケットプレイスをサポート」し、8 月 7 日の 0.147.0 で「ポータブルな Agent Plugins を導入し、ローカル・個人・ワークスペース・リモートのプラグインカタログを横断検索できる」ようになっています。この説明を読んだとき、筆者がまず気にしたのは「同じ名前のプラグインが複数のカタログにあったらどれが勝つのか」でした。プラグイン機構は導入時の解決順が曖昧だと、手元では動いたのに同僚の環境では別物が読み込まれる、という形で事故ります。

公式ドキュメントは配置場所とスキーマは説明していますが、衝突時の解決については触れていません。そこで実際に 2 つのマーケットプレイスを作り、同名プラグインを両方に置いて確かめました。

検証環境

  • Codex CLI: codex-cli 0.147.0npm i -g @openai/codex で導入)
  • Node.js: v22.22.2 / npm 10.9.7
  • 実行環境: Linux コンテナ(Codex へのログインはしていない、未認証状態)

プラグインのインストールとカタログ解決はモデル呼び出しを伴わないため、未認証のままでも一連の操作が通りました。

マーケットプレイスの構造

公式ドキュメントによると、プラグイン本体は .codex-plugin/plugin.json のマニフェストだけが必須で、skills/hooks/ などは任意です。マーケットプレイス定義は次の 2 箇所に置きます。

  • リポジトリ用: $REPO_ROOT/.agents/plugins/marketplace.json
  • 個人用: ~/.agents/plugins/marketplace.json

今回はリポジトリ相当のディレクトリと個人用相当のディレクトリを 1 つずつ作り、どちらにも hello-tool という同名プラグインを登録しました。バージョンだけ 1.0.0 と 2.0.0 で変えて、どちらが選ばれたか判別できるようにしています。

{
  "name": "repo-market",
  "interface": { "displayName": "Repo Marketplace" },
  "plugins": [
    { "name": "hello-tool",
      "source": { "source": "local", "path": "./plugins/hello-tool" },
      "policy": { "installation": "AVAILABLE" },
      "category": "Productivity" }
  ]
}

プラグイン側のマニフェストは最小構成です。

{ "name": "hello-tool", "version": "1.0.0", "description": "REPO origin plugin" }

検証 1: 置くだけでは認識されない

まずリポジトリルートに .agents/plugins/marketplace.json を作り、そのリポジトリ内をカレントディレクトリにして一覧を取りました。

$ codex plugin list --available --json
{
  "installed": [],
  "available": []
}

$ codex plugin marketplace list
No plugin marketplaces in scope.

空です。in scope(スコープ内)という表現から、カレントディレクトリを見て自動的に拾う設計ではないことが分かります。明示的に登録すると通りました。

$ codex plugin marketplace add .
{
  "marketplaceName": "repo-market",
  "installedRoot": "/.../plugtest/repo",
  "alreadyAdded": false
}

add の引数はマーケットプレイスの ルートディレクトリ であって、marketplace.json のパスではありません。ルートを渡すと Codex が配下の .agents/plugins/marketplace.json を読み、name フィールドをマーケットプレイス名として登録します。

検証 2: 登録はグローバルに効く

登録後、設定がどこに書かれたかを確認しました。

$ cat ~/.codex/config.toml
[marketplaces.repo-market]
last_updated = "2026-08-14T03:13:26Z"
source_type = "local"
source = "/.../plugtest/repo"

ユーザーグローバルの ~/.codex/config.toml です。ここで気になるのは、リポジトリの外に出ても同じマーケットプレイスが見えるのかという点でした。試すと見えました。

$ cd /tmp && codex plugin marketplace list
MARKETPLACE  ROOT
repo-market  /.../plugtest/repo

つまり「リポジトリ用の marketplace.json」という配置は、あくまで定義ファイルの置き場所の話です。一度 add すると、そのマシンの全ディレクトリでカタログが有効になります。プロジェクト A で入れたプラグインがプロジェクト B のセッションにも並ぶ、という前提で運用する必要があります。

検証 3: 相対パスの基準はマーケットプレイスのルート

ここが今回いちばんつまずいた箇所です。筆者は最初、プラグイン本体を定義ファイルの隣、つまり .agents/plugins/plugins/hello-tool/ に置きました。marketplace.json に書いた "path": "./plugins/hello-tool" が定義ファイルからの相対パスだと考えたためです。インストールを実行すると失敗しました。

$ codex plugin add hello-tool@repo-market
Error: plugin source path is not a directory: /.../plugtest/repo/plugins/hello-tool

エラーメッセージが解決後の絶対パスを出しているおかげで、基準がすぐ分かります。.agents/plugins/ からではなく、マーケットプレイスのルートadd に渡したディレクトリ)から解決されていました。プラグイン本体をリポジトリルート直下の plugins/hello-tool/ に移すと成功しました。

$ codex plugin add hello-tool@repo-market
Added plugin `hello-tool` from marketplace `repo-market`.
Installed plugin root: /root/.codex/plugins/cache/repo-market/hello-tool/1.0.0

対照実験としてまとめると次のとおりです。

プラグイン本体の配置 marketplace.json の path 結果
.agents/plugins/plugins/hello-tool/ ./plugins/hello-tool 失敗(source path is not a directory
plugins/hello-tool/(ルート直下) ./plugins/hello-tool 成功(cache へ展開)

インストールされた実体は ~/.codex/plugins/cache/<marketplace>/<plugin>/<version>/ にコピーされます。ソースディレクトリを参照し続けるのではなくスナップショットを取る形なので、開発中にプラグイン本体を書き換えても、入れ直すまで反映されない点は押さえておく必要があります。

検証 4: 同名衝突は優先順位ではなくエラー

本題です。個人用マーケットプレイス相当のディレクトリを作り、同名の hello-tool(バージョン 2.0.0)を登録して add しました。

$ codex plugin marketplace add ../personal
{
  "marketplaceName": "personal-market",
  "installedRoot": "/.../plugtest/personal",
  "alreadyAdded": false
}

この状態で一覧を取ると、両方が別々の ID として並びます。JSON が長いので、ID とバージョンだけを抜き出しました。

$ codex plugin list --available --json | python3 -c "import sys,json; d=json.load(sys.stdin); print('installed:',[(p['pluginId'],p['version']) for p in d['installed']]); print('available:',[(p['pluginId'],p.get('version')) for p in d['available']])"
installed: [('hello-tool@repo-market', '1.0.0')]
available: [('hello-tool@personal-market', '2.0.0')]

プラグイン ID が hello-tool@repo-market のように <プラグイン名>@<マーケットプレイス名> で修飾されています。では修飾なしで入れようとするとどうなるか。

$ codex plugin add hello-tool
Error: plugin requires --marketplace unless passed as <plugin>@<marketplace>

優先順位で片方を選ぶのではなく、曖昧な指定を拒否して止まりました。カタログの種別(リポジトリ側か個人側か)による暗黙の勝敗はありません。実行者が明示するまで進まない設計です。

明示すれば両方を同時にインストールでき、共存します。

$ codex plugin add hello-tool@personal-market
Added plugin `hello-tool` from marketplace `personal-market`.
Installed plugin root: /root/.codex/plugins/cache/personal-market/hello-tool/2.0.0
$ codex plugin list --json | python3 -c "import sys,json; d=json.load(sys.stdin); [print(p['pluginId'], p['version'], p['enabled']) for p in d['installed']]"
hello-tool@personal-market 2.0.0 True
hello-tool@repo-market 1.0.0 True

設定ファイルにも両方が有効として並びました。

[plugins."hello-tool@repo-market"]
enabled = true

[plugins."hello-tool@personal-market"]
enabled = true

著者視点の一次所見

実測して考えを改めたのは、「解決順」という言葉で想像していたものと実装の思想が違った点です。筆者は導入前、npm の依存解決のように近い階層のカタログが遠い階層を上書きする形を予想していました。実際には Codex は名前空間をマーケットプレイス名で分け、衝突したら人間に判断させる方向に倒しています。

この設計は運用上ありがたい側面があります。同名プラグインが黙って入れ替わる事故は起きません。一方で、注意が要るのはインストール後です。同名の 2 つが両方 enabled = true のまま共存できてしまうため、両者が同じスキル名やフック名を提供していた場合、どちらが実際に効くのかは codex plugin list を見ただけでは判断できません。チームに配る側としては、プラグイン名の衝突を避ける命名規則(配布元のプレフィックスを付けるなど)を先に決めておくのが安全です。

もう 1 つは、リポジトリに定義ファイルを置く方式が「自動で効く」ものではないことです。README に .agents/plugins/marketplace.json を用意したと書くだけでは、新しく参加したメンバーの手元では何も起きません。codex plugin marketplace add . を実行するところまでをセットアップ手順に含める必要があります。

まとめと注意点

今回の検証で確認できた挙動を整理します。

項目 実測結果
定義ファイルの自動検出 されない。marketplace add の明示登録が必要
登録の保存先 ~/.codex/config.toml[marketplaces.<name>]
登録の有効範囲 マシン全体(カレントディレクトリ非依存)
source.path の基準 マーケットプレイスのルート(add に渡したディレクトリ)
インストール実体 ~/.codex/plugins/cache/<market>/<plugin>/<version>/ にコピー
同名衝突 優先順位で解決せず、<plugin>@<marketplace> を要求してエラー
同名の共存 明示指定すれば両方インストールでき、両方が enabled になる

いずれも Codex CLI 0.147.0 の時点の挙動です。Agent Plugins は追加されたばかりの機能で、カタログ解決の仕様は今後変わる可能性があります。バージョンを上げたときは、少なくとも codex plugin marketplace listcodex plugin list --json で自分の環境に何が有効になっているかを確認しておくと、想定外のプラグインが混ざった状態に気づけます。

関連記事

参考リンク

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?