【Claude Code 連載 第58回】プラグイン依存関係 — バージョンを固定して壊れないようにする

スポンサーリンク
【Claude Code 連載 第58回】プラグイン依存関係 — バージョンを固定して壊れないようにする 用語解説
【Claude Code 連載 第58回】プラグイン依存関係 — バージョンを固定して壊れないようにする
この記事は約16分で読めます。
よっしー
よっしー

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

スポンサーリンク

背景

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

1. 一言でいうと何か

プラグインは他のプラグインに依存できます。そして依存関係にはsemver のバージョン制約を付けられます。今回はその仕組みの話です。

まず制約を付けなかった場合に何が起きるかが問題設定です。デフォルトでは、依存関係は最新の利用可能なバージョンを追跡します。つまりアップストリームのリリースが、警告なしにあなたのプラグインの依存関係を変えてしまう

公式が挙げている例が具体的で分かりやすい。内部マーケットプレイスに2チームがいるとします。プラットフォームチームは secrets-vault(シークレットバックエンドをラップする MCP サーバー)を、デプロイチームは deploy-kit(デプロイ中に secrets-vault を呼んで認証情報を取る)を保守しています。deploy-kitsecrets-vault v2.1.0 でテスト済み。

制約がないと、プラットフォームチームが MCP ツール名を変えるリリースにタグを打った瞬間、次の自動更新で全エンジニアの secrets-vault が新バージョンに移り、deploy-kit が壊れます。

制約を付ければ、deploy-kit は「secrets-vault~2.1.0 の範囲であること」を宣言し、**エンジニアは最高の一致する 2.1.x パッチに留まります。**デプロイチームは、より広い制約を持つ新しい deploy-kit を公開することで、自分たちのスケジュールでアップグレードします。

インストール時の挙動も押さえておきます。依存関係を宣言したプラグインを入れると、Claude Code が依存関係を自動解決してインストールし、インストール出力の末尾に追加されたものを列挙します。後で依存関係が見つからなくなった場合は、/reload-plugins とバックグラウンドの自動更新が、そのマーケットプレイスが設定済みなら再インストールします。ただし、追加していないマーケットプレイスからの依存関係は未解決のままです。

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

シーン1:アップストリームの破壊的変更から自分のプラグインを守る

冒頭の例そのものです。チーム間でプラグインが依存し合う内部マーケットプレイスでは、制約なしの運用は事故が起きるまでの時間の問題になります。

シーン2:ロール別のプラグインセットを1インストールで配る

**マニフェストは namedependencies だけでも成立します。**これを使うと、キュレーションしたプラグイン一式を1つのインストールの背後にパッケージ化できます。「バックエンドエンジニアはこれを1つ入れれば全部揃う」という配り方です。

シーン3:組織全体に標準セットをロールアウトする

バンドルプラグインを管理設定の enabledPlugins に追加すれば、組織全体に配れます。

シーン4:不要になった依存関係を掃除する

自動インストールされた依存関係は、親をアンインストールしてもディスクに残ります(再インストールや直接利用のため)。claude plugin prune で片付けられます。

不要・向かないケース

  • npm マーケットプレイスソースでバージョンを制御したいタグベースの解決は git バックアップソースにのみ適用されるので、**制約は「どのバージョンが取得されるか」を制御しません。**制約は読み込み時にチェックされ、満たさなければ dependency-version-unsatisfied で無効化されるだけです。
  • タグを打っていないアップストリームに制約をかけたいバージョン制約は git タグに対して解決されるので、規約どおりのタグがないと解決できません。
  • git リポジトリでないローカルフォルダのマーケットプレイスで制約を効かせたいタグが存在しないので、フォルダの現在の内容からインストールされます。
  • 信頼の連鎖を期待する場合クロスマーケットプレイス依存は、ルートマーケットプレイスのアローリストしか参照されません。信頼は中間マーケットプレイスを通じてチェーンしません。
  • 自分で入れたプラグインを prune で消したい消えません。prune の対象は他のプラグインの dependencies 経由で自動インストールされたものだけです。

3. 宣言と運用の実例

依存関係を宣言する

.claude-plugin/plugin.jsondependencies 配列に書きます。各エントリはプラグイン名の文字列か、バージョン制約を持つオブジェクトのどちらかです。

{
  "name": "deploy-kit",
  "version": "3.1.0",
  "dependencies": [
    "audit-logger",
    { "name": "secrets-vault", "version": "~2.1.0" }
  ]
}

文字列だけの "audit-logger" は、そのプラグインのマーケットプレイスが提供するバージョンに依存します。オブジェクト形式のフィールドは3つ。

フィールド説明
namestringプラグイン名。宣言するプラグインと同じマーケットプレイス内で解決される。必須
versionstringsemver 範囲(~2.1.0^2.0>=1.4=2.1.0 など)。この範囲を満たす最高のタグ付きバージョンで取得される
marketplacestringname を解決する別のマーケットプレイス。アローリストに載っていないとブロックされる

versionNode の semver パッケージがサポートする任意の式を受け付けます——キャレット、チルダ、ハイフン、比較演算子の範囲。

そして見落としやすい規則が1つ。^2.0.0-0 のようにプレリリースサフィックスで範囲がオプトインしない限り、2.0.0-beta.1 などのプレリリースバージョンは除外されます。

チーム用バンドルを作る

必須の name 以外は dependencies 配列だけ、というマニフェストが成立します。

{
  "name": "backend-standard",
  "version": "1.0.0",
  "description": "Standard plugin set for backend engineers",
  "dependencies": [
    "secrets-vault",
    "deploy-kit",
    { "name": "db-migrate", "version": "^3.0" },
    "oncall-runbook"
  ]
}

backend-standard を入れれば4つの依存関係すべてが解決されてインストールされます。

セットにツールを追加するには、依存関係を増やした新バージョンを公開します。ただしここに実務上の落とし穴があります——非 Anthropic マーケットプレイスは自動更新がデフォルトでオフ(第57回)なので、エンジニアが新バージョンを受け取る経路は2つに限られます。/plugin でそのマーケットプレイスの自動更新を有効にするか、claude plugin update backend-standard を実行してから /reload-plugins を実行するか。

バンドルを組織全体にロールアウトするなら、管理設定の enabledPlugins にバンドルプラグインを追加します。

別のマーケットプレイスに依存する

**デフォルトでは拒否されます。**理由も明快で、1つのマーケットプレイスが、確認していないソースからプラグインを静かに引き込むのを防ぐためです。

許可するには、ルートマーケットプレイス(ユーザーがインストールしているプラグインをホストしているマーケットプレイス)の保守者が allowCrossMarketplaceDependenciesOn にターゲット名を追加します。

{
  "name": "acme-tools",
  "owner": { "name": "Acme" },
  "allowCrossMarketplaceDependenciesOn": ["acme-shared"],
  "plugins": [
    {
      "name": "deploy-kit",
      "source": "./deploy-kit",
      "dependencies": [
        { "name": "audit-logger", "marketplace": "acme-shared" }
      ]
    }
  ]
}

重要な設計判断が明記されています。**参照されるのはルートマーケットプレイスのアローリストのみ。信頼は中間マーケットプレイスを通じてチェーンしません。**A が B を許可し、B が C を許可していても、A から C は通りません。

フィールドが無いか対象が含まれていなければ、インストールは cross-marketplace エラーで失敗し、設定すべきフィールド名を教えてくれます。なおユーザーが手動で依存関係を先にインストールすれば、アローリストを変えずに制約は満たされます。

リリースにタグを打つ(アップストリーム側の責務)

バージョン制約はマーケットプレイスリポジトリの git タグに対して解決されます。したがってアップストリームがタグを打っていなければ、依存側は何も解決できません。

規約は {plugin-name}--v{version} で、{version}そのコミットの plugin.jsonversion と一致していなければなりません。プラグインディレクトリからこれを実行します。

claude plugin tag --push

このコマンドはマニフェストと、それを囲むマーケットプレイスエントリからタグ名を導出します。そしてタグ作成前に4つのチェックをします——プラグイン内容の検証plugin.json とマーケットプレイスエントリのバージョン一致確認プラグインディレクトリ配下のクリーンな作業ツリーの要求タグが既に存在する場合の拒否--dry-run で確認だけもできます。両者を自分で同期させておけば git tag secrets-vault--v2.1.0 を直接打つのと同等です。

タグ名の設計にも配慮があります。プラグイン名プレフィックスにより、1つのマーケットプレイスリポジトリが独立したバージョン行を持つ複数プラグインをホストできる。そして--v セパレータは完全なプラグイン名のプレフィックスマッチとして解析されるので、ハイフンを含むプラグイン名も正しく扱われます。

解決の流れはこうです。{ "name": "secrets-vault", "version": "~2.1.0" } を宣言したプラグインをインストールすると、Claude Code はマーケットプレイスのタグを列挙し、secrets-vault--v で始まるものに絞り、~2.1.0 を満たす最高バージョンを取得します。一致するタグが無ければ、依存プラグインは「利用可能なバージョンを列挙するエラー」とともに無効化されます。

ローカルフォルダのマーケットプレイスについては段差があります。フォルダが git リポジトリなら同じようにタグを解決しますが(v2.1.196 以降)、次の2ケースではフォルダの現在の内容から依存関係がインストールされます。v2.1.196 より前のバージョン(ローカルフォルダからタグを読まないため、制約付き依存関係はそのコピーが範囲を満たす場合にのみ読み込まれる)と、git リポジトリでないローカルフォルダ(タグが存在しない)。

実装上の細かい配慮も2つ書かれています。解決されたタグの semver は plugin.jsonversion とは別に記録されるので、そのコミットの plugin.json が古い値を持っていても、制約チェックは実際に取得したタグを使います。そしてタグ解決インストールのキャッシュディレクトリ名には12文字のコミット SHA サフィックスが含まれるので、保守者がタグを別のコミットへ強制移動しても、次のインストールは古い内容を再利用せず新しいキャッシュディレクトリを取ります。

制約が複数あるとどうなるか

複数のインストール済みプラグインが同じ依存関係を制約する場合、Claude Code は範囲を交差させ、すべてを満たす最高バージョンに解決します。

プラグイン A の要求プラグイン B の要求結果
^2.0>=2.1**2.1.0 以上の最高 2.x タグで1つのインストール。**両方が読み込まれる
~2.1~3.0**プラグイン B のインストールが range-conflict で失敗。**プラグイン A と依存関係は以前のまま
=2.1.0なし依存関係は 2.1.0 に留まる。プラグイン A がインストールされている間、自動更新は新バージョンをスキップ

2行目の挙動が親切です。衝突しても既存の環境は壊れません——失敗するのは後から入れようとした側だけ。

自動更新の扱いも一貫しています。制約付き依存関係は、マーケットプレイスの最新版ではなく「インストール済みプラグインのすべての範囲を満たす最高 git タグ」で取得されるので、**許可された範囲内では更新を受け続けます。**すべての範囲を満たすタグが無い場合は、その依存関係をスキップし、スキップを /plugin のエラータブに表示して、制約しているプラグイン名を挙げます。

そして解放の条件。その依存関係を制約する最後のプラグインをアンインストールすると、依存関係は固定されなくなり、次の更新からマーケットプレイスエントリの追跡を再開します。

有効化と無効化の連鎖

v2.1.143 以降、有効化は下方向に伝播し、無効化は上方向にブロックされます。

プラグインを有効にすると、Claude Code は同じスコープでその依存関係も有効にします。依存関係がさらに依存を持つならそれも有効化(推移的)。成功メッセージには、指定したプラグインと一緒に有効化されたものが列挙されます。

依存関係を有効化できない場合は拒否され、理由と直し方が出ます。

条件結果
依存関係がインストールされていない失敗し、各欠落分の claude plugin install コマンドを出力
依存関係が組織のプラグインポリシーでブロックされている失敗し、ブロックされた依存関係を名指し
依存関係が、ターゲットスコープより優先度の高いスコープで false になっている失敗。そのスコープで有効にするか、--scope でそこに書き込む
すべてインストール済みで許可されている成功。プラグインと、ターゲットスコープでまだ有効でない各依存関係に true を書き込む

ここで第55回の defaultEnabled と接続します。**依存関係がマニフェストで defaultEnabled: false を設定していても、Claude Code は明示的な true を書き込むので有効になります。**インストール時も同じで、アクティブなプラグインを満たすために取得された依存関係は、自身のデフォルトに関係なく true でインストールされます。

無効化は逆向きです。別の有効なプラグインがまだ依存している場合、Claude Code は拒否します。そしてエラーが連鎖コマンドを提示してくれます。

secrets-vault is still required by deploy-kit. Disable that plugin first, or
disable everything together: claude plugin disable deploy-kit@acme-tools && claude plugin disable secrets-vault@acme-tools

このコマンドをコピーすれば1ステップで一式を無効化できます。エラーメッセージが解決策そのものを提供する設計です。

なお v2.1.143 より前は、指定したプラグインだけを有効化・無効化し、次の読み込み時に dependency-unsatisfied エラーを出す挙動でした。

孤立した依存関係を掃除する

自動インストールされた依存関係は、それを入れたプラグインをアンインストールした後もディスクに残ります。理由は依存プラグインを再インストールするかもしれない、あるいは依存関係を直接使い続けるかもしれないから。

掃除はこれです(v2.1.121 以降)。

claude plugin prune

孤立した自動インストール依存関係を列挙し、確認プロンプトの後に削除します。デフォルトはユーザースコープで、--scope project / --scope local で切り替え。--dry-run で確認のみ、-y で確認をスキップ。stdin か stdout がターミナルでない場合は、孤立分を列挙して終了し、-y が無ければ削除しません——CI で誤って消さないための配慮です。

アンインストールと同時に掃除するなら --prune です。

claude plugin uninstall deploy-kit --prune

指定したプラグインを削除した後、自動インストール依存関係をスキャンして、その時点で孤立したものを削除します。繰り返しになりますが、自分でインストールしたプラグインは決して prune されません。

依存関係エラーの読み方

問題は claude plugin list/plugin インターフェースに出ます。Claude Code は、エラーを解決するまで影響を受けたプラグインを無効にします。

エラー意味解決方法
dependency-unsatisfied依存関係が未インストール、またはインストール済みだが無効エラーに出ている claude plugin install を実行。マーケットプレイス未設定なら claude plugin marketplace add で追加すれば自動解決。無効なら有効化
range-conflictバージョン要件を組み合わせられない。原因は3種——どのバージョンも全範囲を満たさない/範囲が有効な semver でない/結合された範囲が複雑すぎて交差できない競合するプラグインの一方をアンインストールまたは更新、無効な version 文字列を修正、長い || チェーンを簡略化、あるいはアップストリーム作者に制約を広げるよう依頼
dependency-version-unsatisfiedインストール済み依存関係のバージョンが、このプラグインの範囲外claude plugin install <dependency>@<marketplace>すべての現在の制約に対して再解決
no-matching-tag依存関係のリポジトリに、範囲を満たす {name}--v* タグが無いアップストリームが規約どおりタグを打っているか確認するか、範囲を緩める

プログラムから確認するなら claude plugin list --json の各プラグインの errors フィールドを読みます。

4. まとめ + 次回予告

  • **制約を付けないと、依存関係は常に最新を追う。**アップストリームのリリースが警告なしに自分のプラグインを壊しうる。
  • 宣言は plugin.jsondependencies文字列(名前のみ)かオブジェクト(name / version / marketplaceversionNode の semverで、プレリリースは明示的にオプトインしない限り除外
  • namedependencies だけのマニフェストでチーム用バンドルが作れる。組織配布は管理設定の enabledPlugins
  • **クロスマーケットプレイス依存はデフォルト拒否。**ルートマーケットプレイスの allowCrossMarketplaceDependenciesOn で許可する。信頼は中間を通じてチェーンしない。
  • 制約は git タグ({plugin-name}--v{version})に対して解決される。claude plugin tag --push が検証つきでタグを打つ。タグが無ければ依存側は解決できない。
  • 複数の制約は交差される。衝突時に失敗するのは後から入れる側で、既存環境は壊れない。自動更新は範囲内でのみ動く。
  • 有効化は依存関係へ推移的に伝播し、defaultEnabled: false も上書きする。無効化は依存元があるとブロックされ、エラーが連鎖コマンドを提示する。
  • 自動インストール依存関係はアンインストール後も残る。claude plugin prune または uninstall --prune で掃除。自分で入れたものは対象外。
  • npm ソースでは制約が取得バージョンを制御しない——読み込み時のチェックのみ。
  • エラーは4種(dependency-unsatisfied / range-conflict / dependency-version-unsatisfied / no-matching-tag)。claude plugin list --jsonerrors で機械的に読める。

第54回から始まったプラグイン系は、これで作る・包む・配る・受け取る・依存を管理する、が揃いました。

次回予告(暫定):**組織向けプラグインの推奨(plugin-relevance)**を取り上げ、第56回・第57回で出てきた relevance フィールドと pluginSuggestionMarketplaces の仕組み——Claude Code がどういう条件でプラグインをユーザーに提案するか——を扱う予定です。

※連載の実際の次テーマは未確定です。


よっしー
よっしー

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

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

コメント

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