CLAUDE_CODE_SYNC_PLUGIN_INSTALLで-pの初回クエリ前にプラグインを入れ終える
claude -pはプラグインをバックグラウンドで入れるため、初回ターンに間に合わないことがあります。値1とTIMEOUT_MSで待ち方を決める手順です。
CIで claude -p を回したとき、リポジトリの設定で有効にしたはずのプラグインが最初の質問では使えない。そんな挙動は、プラグインの導入がバックグラウンドで進むことが原因になりえます。CLAUDE_CODE_SYNC_PLUGIN_INSTALL=1 を付けると、-p の実行は初回クエリの前にプラグインの導入完了を待ちます。待ち時間の上限は CLAUDE_CODE_SYNC_PLUGIN_INSTALL_TIMEOUT_MS で決めます。
CLAUDE_CODE_SYNC_PLUGIN_INSTALLは何を変える変数か
CLAUDE_CODE_SYNC_PLUGIN_INSTALL は、非対話モード(-p)でプラグインの導入を同期にする環境変数です。値に 1 を入れると、初回クエリの前に導入が終わるまで待ちます。
変数を付けない -p の実行では、マーケットプレイスとプラグインはバックグラウンドで入ります。導入が終わる前に最初のターンが始まるため、そのターンではプラグインが使えない場合があります。
-p 実行でのプラグイン導入の違い
変数なし
導入はバックグラウンドで進みます。初回ターンにプラグインのスキルやコマンドが間に合わないことがあります。
SYNC_PLUGIN_INSTALL=1
初回クエリの前に導入の完了を待ちます。上限は CLAUDE_CODE_SYNC_PLUGIN_INSTALL_TIMEOUT_MS で指定します。
対象はあくまで -p です。対話セッションでは、この変数を使う場面は説明されていません。
設定から入るプラグインが対象になる場面
同期待ちが効くのは、設定ファイルで宣言したマーケットプレイスとプラグインです。組織の管理設定や、リポジトリの .claude/settings.json に次のような宣言を置いている場合が典型です。
{
"extraKnownMarketplaces": {
"your-marketplace": {
"source": { "source": "github", "repo": "your-org/your-marketplace" }
}
},
"enabledPlugins": {
"code-formatter@your-marketplace": true
}
}-p やCIの実行では、管理設定の extraKnownMarketplaces と enabledPlugins はセッション開始時に適用されますが、導入自体はバックグラウンドで走ります。プラグインのドキュメントも、-p やCIでは初回ターンにプラグインが欠けうるので CLAUDE_CODE_SYNC_PLUGIN_INSTALL=1 を付けるよう案内しています。
リポジトリ側の設定には条件があります。extraKnownMarketplaces は、信頼済みのフォルダでしか適用されません。-p ではワークスペースの信頼ダイアログが出ないため、事前に対話で信頼を承諾したフォルダか、~/.claude.json に hasTrustDialogAccepted を設定したフォルダに限られます。変数を付けても、マーケットプレイスの登録自体が無視されていれば待つ対象がありません。
値1とTIMEOUT_MSの組み合わせ
基本の形は、-p の前に環境変数を付けるだけです。
CLAUDE_CODE_SYNC_PLUGIN_INSTALL=1 \
CLAUDE_CODE_SYNC_PLUGIN_INSTALL_TIMEOUT_MS=120000 \
claude -p "lint を実行して結果を要約して"上限を付ける理由は、既定に上限がないことです。CLAUDE_CODE_SYNC_PLUGIN_INSTALL_TIMEOUT_MS には既定値がなく、未設定なら導入が終わるまで待ち続けます。マーケットプレイスの取得が止まると、ジョブも止まります。
待ち方の選択肢
TIMEOUT_MS を付けない
導入が終わるまで待ちます。ジョブ側のタイムアウトで打ち切ることになります。
TIMEOUT_MS を付ける
超過するとプラグインなしで先へ進み、エラーをログに出します。
超過後の挙動は「プラグインなしで続行する」です。失敗として落ちるわけではないので、プラグインが必須のジョブでは、次節の確認を併せて入れておくと、欠けたまま通る事態を防げます。
導入の進行と成否を機械的に確かめる
同期を有効にすると、導入の途中経過が system/plugin_install イベントとして流れます。--output-format stream-json --verbose で読めます。
| フィールド | 内容 |
|---|---|
status | 内容started と completed が全体を挟み、installed と failed がマーケットプレイス単位 |
name | 内容マーケットプレイス名(installed と failed のとき) |
error | 内容失敗メッセージ(failed のとき) |
system/init は通常ストリームの先頭に来るイベントですが、起動時のイベントが先に流れる場合があります。plugin_install はその一つで、同期を有効にしたときは init より前に届きます。つまり、init を待ってからプラグインの状態を見る処理でも、導入の経過は取りこぼさずに読めます。進捗を自前の画面に出したいなら、started で表示を始め、completed で閉じる形が素直です。
イベントだけ抜き出す例です。形は例示で、実際の出力は環境で変わります。
CLAUDE_CODE_SYNC_PLUGIN_INSTALL=1 \
CLAUDE_CODE_SYNC_PLUGIN_INSTALL_TIMEOUT_MS=120000 \
claude -p "ping" --output-format stream-json --verbose \
| jq -c 'select(.type=="system" and .subtype=="plugin_install")'最終的な成否は、system/init イベントの plugins と plugin_errors で見ます。読み込めたプラグインは plugins に入り、読み込めなかったものは plugins に現れません。エラーがあるときだけ plugin_errors に plugin、type、message が入ります。CIで「必要なプラグインが plugins に無ければ失敗させる」判定を作るなら、この2つが拠り所です。
似た変数との使い分け
名前の近い変数やフラグがいくつかあり、役割は別です。
| 手段 | 何をするか |
|---|---|
CLAUDE_CODE_SYNC_PLUGIN_INSTALL | 何をするか設定で宣言したプラグインの導入完了を、初回クエリの前に待つ |
CLAUDE_CODE_SYNC_SKILLS | 何をするかclaude.aiアカウントのスキルの一覧を、初回クエリの前に待つ。claude.ai認証が前提 |
--plugin-dir | 何をするかローカルのディレクトリを、そのセッションだけ読み込む |
CLAUDE_CODE_PLUGIN_SEED_DIR | 何をするか事前に作ったプラグインの置き場から、cloneなしで登録する |
スキル側の待ちは別変数で、2つのタイムアウトの使い分けはCLAUDE_CODE_SYNC_SKILLSの2つのタイムアウト変数を使い分けるにあります。ローカルのプラグインを直接渡すならCLAUDE_CODE_PLUGIN_DIRSで複数プラグインをセッション単位で読み込むの方法で、導入待ちそのものが発生しません。
コンテナイメージやCIランナーで、実行時にcloneしたくない場合は CLAUDE_CODE_PLUGIN_SEED_DIR に事前準備のディレクトリを向ける方法があります。ネットワークに出ない分、同期待ちの問題は小さくなります。
--bare との併用にも注意が要ります。--bare は、インストール済みプラグインの自動検出を含めて起動時の探索を省くモードです。プラグインを使いたいなら、--plugin-dir か --plugin-url で明示的に渡す形になります。
つまずきやすい点
- 環境変数の置き場:
CLAUDE_CODE_SYNC_SKILLSやCLAUDE_CODE_SYNC_PLUGINSのように、起動や同期の仕方を変える変数は、プロジェクト設定とローカル設定のenvでは無視されます。同期待ちの変数も同じ系統なので、ジョブの環境変数(シェルやCIのステップのenv)に置くのが確実です。 - 外部ソースのプラグイン: マーケットプレイスが外部リポジトリを指すプラグインは、リポジトリの設定だけでは入りません。
Plugin "<name>" is enabled in project settings but isn't installedと表示され、claude plugin install <name>@<marketplace> --scope projectが必要です。同期待ちでは解決しません。 - 非公開マーケットプレイス: CIでは、プラグインを入れる前にgitの認証ヘルパーを設定します。GitHub Actionsなら、読み取り権限のあるトークンを
GH_TOKENに入れてgh auth setup-gitを実行します。既定のワークフロートークンは、そのワークフロー自身のリポジトリにしか届きません。 - 導入失敗の切り分け: tmpfsを使う環境などでは導入が失敗することがあります。原因と対処はEXDEVでtmpfs環境のプラグインインストールが失敗する原因と対処にまとめています。
症状から原因を切り分ける
「プラグインのスキルが呼ばれない」という症状には、原因がいくつか重なります。同期待ちが関係するのは、その一部です。
最初に見るのは system/init の plugins です。目的のプラグインが並んでいなければ、そのターンでは読み込まれていません。変数なしで並ばず、CLAUDE_CODE_SYNC_PLUGIN_INSTALL=1 を付けると並ぶなら、導入が間に合っていなかったと分かります。
付けても並ばないときは、次の順に疑います。
plugin_installイベントにfailedがあるか。あればnameとerrorで、どのマーケットプレイスが落ちたかが分かりますplugin_errorsに項目があるか。依存バージョンの不一致などの読み込み時エラーがここに出ます- 上限に当たっていないか。
CLAUDE_CODE_SYNC_PLUGIN_INSTALL_TIMEOUT_MSを超えると、エラーをログに出してプラグインなしで進みます - リポジトリ設定の
extraKnownMarketplacesが、信頼済みでないフォルダで黙って無視されていないか
4つ目は特に気づきにくい点です。信頼されていないフォルダでは、メッセージなしで無視されます。変数の問題に見えて、実際にはマーケットプレイスが登録されていないだけ、という場合があります。この場合は変数を足しても結果は変わりません。
GitHub Actionsでの組み込み例
最後に、非公開マーケットプレイスのプラグインを使うActionsのステップの形です。トークン名やリポジトリ名は置き換えて使います。
- name: git の認証を設定
run: gh auth setup-git
env:
GH_TOKEN: ${{ secrets.MARKETPLACE_READ_TOKEN }}
- name: プラグイン込みで claude -p を実行
env:
CLAUDE_CODE_SYNC_PLUGIN_INSTALL: "1"
CLAUDE_CODE_SYNC_PLUGIN_INSTALL_TIMEOUT_MS: "120000"
GH_TOKEN: ${{ secrets.MARKETPLACE_READ_TOKEN }}
run: |
claude -p "変更差分をレビューして" \
--output-format stream-json --verbose > out.jsonl変数は env に置くので、前節の「プロジェクト設定の env では無視される」問題を避けられます。同じジョブで out.jsonl から system/init の行を jq で取り出し、plugins を検査するステップを続ければ、前節の流れが一通り揃います。
120000ミリ秒(2分)は例としての値です。マーケットプレイスの大きさやランナーのネットワークで適切な値は変わるため、ジョブの実績を見て調整します。上限を長くするほど、導入が止まったときにジョブ全体が遅れる点は変わりません。
組み込みの順序
CIのジョブに入れるなら、次の順が無駄がありません。
CI に同期待ちを組み込む流れ
- 1
宣言を置く
管理設定かリポジトリの設定に
extraKnownMarketplacesとenabledPluginsを書きます。 - 2
認証と信頼を整える
非公開ならgitの認証ヘルパーを設定します。リポジトリ設定なら信頼済みであることも確かめます。
- 3
変数を付けて実行する
CLAUDE_CODE_SYNC_PLUGIN_INSTALL=1とCLAUDE_CODE_SYNC_PLUGIN_INSTALL_TIMEOUT_MSを付けてclaude -pを呼びます。 - 4
init で検証する
system/initのpluginsに目的のプラグインがあるかを見て、無ければジョブを失敗させます。
プラグインの提供元を組織で絞っている環境では、strictPluginOnlyCustomizationでskillsやhooksをプラグインに絞るも合わせて確認すると、同期待ちで揃えたプラグインだけが効く構成を作れます。
まとめ
-p の初回ターンでプラグインを確実に使いたいなら、CLAUDE_CODE_SYNC_PLUGIN_INSTALL=1 で導入完了を待たせ、_TIMEOUT_MS で待ちに上限を付けます。上限を超えるとプラグインなしで進むので、system/init の plugins を見る検証までを1組にして初めて、プラグイン前提のジョブとして成立します。