Claude Media
セットアップスクリプトとフックの使い分け — Claude Code

セットアップスクリプトとフックの使い分け — Claude Code

Claude Codeのクラウド環境で依存関係を用意する方法は2つ。VM自体を作るSetup scriptと、セッションのたびに走るSessionStartフックの使い分け基準をまとめました。

セットアップスクリプト(Setup script)とSessionStartフック、何が違うか

Claude Codeのクラウドセッションで依存関係を用意する方法は2つあります。ひとつは環境設定に書くセットアップスクリプト(Setup script)、もうひとつはリポジトリの .claude/settings.json に書くSessionStartフックです。どちらも「セッション開始時に何かを実行する」点は同じですが、走る場所とタイミング、そしてキャッシュされるかどうかがまったく異なります。

結論から言うと、VM自体を作り込む(OSレベルのツールを入れる)ならSetup script、プロジェクトの依存関係をローカルとクラウド両方で揃えるならSessionStartフックです。両方を組み合わせて使う構成も一般的で、Setup scriptで重いツールチェーンをキャッシュに焼き込み、SessionStartフックで軽量な npm install を毎回走らせる、という役割分担になります。

セットアップスクリプトはどこで走り、フックはいつ走るか

  • Setup script: 環境設定ダイアログに書くBashスクリプト。Claude Codeが起動する前、キャッシュされた環境が無いときだけ実行される
  • SessionStartフック: .claude/settings.json に書くフック設定。Claude Code起動後、セッションの開始・再開のたびにローカルとクラウドの両方で実行される

どの軸で比べるか

評価軸Setup scriptSessionStartフック
設定場所Setup scriptclaude.ai/codeの環境ダイアログ(組織共有環境はOwnerロールの管理ページ)SessionStartフックリポジトリの .claude/settings.json
実行タイミングSetup scriptClaude Code起動前、キャッシュ済み環境が無いときのみSessionStartフックClaude Code起動後、毎回のセッション開始・再開時
実行環境Setup scriptクラウドセッションのみSessionStartフックローカル・クラウド両方
実行時間の制約Setup script概ね5分以内に収める必要があるSessionStartフック制約なし(ただし起動レイテンシーに直結)
失敗時の挙動Setup script0以外の終了コードでセッション起動自体が失敗するSessionStartフックフックの失敗はセッション起動を止めない
キャッシュSetup script環境スナップショットとして永続化される(約7日で失効)SessionStartフックキャッシュされない。毎回フルで実行される

強み・弱み

Setup scriptの強み

初回実行後は結果がファイルシステムのスナップショットとしてキャッシュされ、以降のセッションはインストール済みの状態から始まります。apt install でOSレベルのツールを入れたり、時間のかかるDockerイメージのpullを済ませておいたりする用途に向きます。ScriptはUbuntu 24.04上でrootとして走るため、権限の制約はほとんどありません。

弱みは実行時間の制約です。ネットワーク越しのインストールを含め、おおむね5分以内に完了させる必要があります。超える処理は並列化(&wait)するか、単発の重いダウンロードは後述のSessionStartフックでバックグラウンド起動する形に逃がします。またキャッシュは「ファイルとして書き込まれたもの」だけを保持し、起動していたプロセスは保持しません。スクリプトが起動したデータベースやDocker Composeのスタックは、次のセッションでは自動的には立ち上がりません。

SessionStartフックの強み

ローカルとクラウドの両方で同じフックが走るため、「ローカルでは手動で npm install していたが、クラウドセッションでは忘れていた」という食い違いを防げます。.claude/settings.json に1回書けば、チームの全員・全セッションに同じ挙動が適用されます。

弱みは、キャッシュが効かず毎回フルで実行される点です。起動のたびにインストールが走るため、セッション起動のレイテンシーに直結します。依存関係が既にあるかを先に確認してからインストールする、といった工夫が要ります。またクラウド専用の絞り込み機能は無く、素のままではローカルでも同じスクリプトが走ります。

セットアップスクリプトを書くときの4つの制約

Setup scriptは自由なBashスクリプトですが、書く前に押さえておくべき制約が4つあります。

  • 終了コードを0にする: 0以外で終了するとセッションの起動自体が失敗します。不安定になりがちなコマンドの末尾には || true を付け、途中の失敗が全体を止めないようにします
  • 実行時間はおおむね5分以内: 環境キャッシュを正しく構築するため、スクリプト全体の実行時間を5分程度に収める必要があります。独立したインストール処理は &wait で並列化し、それでも収まらない単発の重いダウンロードは、バックグラウンドで走らせるSessionStartフックに切り出します
  • インストールにはネットワークアクセスが要る: パッケージのインストールはレジストリへの到達が前提です。既定のTrustedレベルはnpm・PyPI・RubyGems・crates.ioなど主要レジストリをカバーしますが、ネットワークアクセスをNoneにしている環境ではインストール系のコマンドがすべて失敗します
  • ベースイメージそのものは差し替えられない: Setup scriptはあくまで提供イメージの上にツールを足す仕組みで、ベースイメージ自体を別物に入れ替える用途には対応していません。別のベースイメージが要る場合は、提供イメージの上に追加するか、docker compose で自前のイメージを併走させる形で回避します

クラウドだけでフックを実行する書き方

SessionStartフックはローカル・クラウド両方で走るため、クラウドだけに限定したい場合は環境変数 CLAUDE_CODE_REMOTE で分岐します。この変数はクラウドセッションのVMで true になり、ローカルでは決して true になりません。

まず、リポジトリの .claude/settings.json にSessionStartフックを追加します。

{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "startup|resume",
        "hooks": [
          {
            "type": "command",
            "command": "bash \"$CLAUDE_PROJECT_DIR\"/scripts/install_pkgs.sh"
          }
        ]
      }
    ]
  }
}

次に、scripts/install_pkgs.sh 側でクラウド以外を早期リターンさせます。

#!/bin/bash
 
if [ "$CLAUDE_CODE_REMOTE" != "true" ]; then
  exit 0
fi
 
npm install
pip install -r requirements.txt
exit 0

この2つを組み合わせると、クラウドセッションだけが起動のたびに npm installpip install を実行し、ローカルセッションは何もせずスクリプトを抜けます。

Dockerイメージと追加パッケージのキャッシュ挙動

Setup scriptで用意したものは、種類によってキャッシュのされ方が変わります。docker compose pulldocker compose build をSetup scriptに書いておくと、pull済みのイメージはファイルとしてディスクに残るため、環境キャッシュの対象になります。ただしキャッシュが保持するのはファイルだけで、実行中のコンテナやプロセスは対象外です。次のセッションでもコンテナは自動起動せず、Claudeが docker compose up を実行するかSessionStartフックで起動する必要があります。

Setup scriptで入れていないパッケージをセッションの途中でClaudeに追加インストールさせることもできますが、その場でインストールした分は他のセッションには引き継がれません。恒常的に必要なパッケージは、思いついた時点でSetup script側に足しておくのが安全です。

組織共有環境でのセットアップスクリプト

Claude Tagのチャンネルなど、組織単位のセッション向けのSetup scriptは、共有環境の管理ページから設定します。Claude TagはGitHubのIssue・PRでメンションすると起動するクラウドセッションです。ただしこのページを開けるのはOwner(オーナー)ロールに限られ、Adminロールでは管理ページ自体を開けません。環境セレクター側の設定はAdmin・Ownerが編集でき、それ以外のメンバーは読み取り専用です。用途の典型例は、.NETのようにプリインストールされていないツールチェーンをチャンネル用の環境にまとめて入れておくケースです。個人の環境設定と異なり、共有環境のSetup scriptは管理ページから編集し、そのチャンネルを使う全メンバーに同じ環境が適用されます。個々のメンバーが自分のセッションだけ設定を変えることはできません。

使い分け早見表

やりたいこと使うべき仕組み
OSパッケージ(apt install 対象)を入れる使うべき仕組みSetup script
Node.js / Rubyなど言語自体のバージョンを切り替える使うべき仕組みSetup script
重いDockerイメージを事前にpullしておく使うべき仕組みSetup script(docker compose pull を実行)
npm install などプロジェクトの依存関係をローカル・クラウド共通で揃える使うべき仕組みSessionStartフック
ユーザー単位のカスタムコマンド・エイリアスを用意する使うべき仕組みクラウドでも効かせたいならリポジトリの .claude/settings.json に書く(~/.claude/settings.json はローカル止まり)
クラウドセッションだけに限定した処理をしたい使うべき仕組みSessionStartフック + CLAUDE_CODE_REMOTE 判定

よくあるつまずき

  • ユーザーレベルの ~/.claude/settings.json のSessionStartフックが動かない: ユーザーレベルの設定はローカルマシンに留まり、クラウドセッションには届きません。クラウドで使うフックはリポジトリの設定ファイルに書く必要があります
  • 許可ドメインを変更したら次のセッション起動が急に遅くなった: Setup scriptはEnvironmentのSetup script自体や許可ドメインを変更したとき、そしてキャッシュが約7日で失効したときに再実行されます。既存セッションを再開しただけでは再実行されないため、起動が遅くなったのはキャッシュが再構築された合図です
  • Setup scriptが起動したはずのデータベースが次のセッションで動いていない: キャッシュはファイルシステムのスナップショットで、実行中だったプロセスは保持されません。プロセスの起動はセッションごとにSessionStartフックか、Claudeへの指示で行います
  • 組織共有環境のSetup scriptを個人設定で上書きできない: 共有環境のSetup scriptは管理ページ側の設定が正で、メンバー個人がそのセッションだけ内容を変えることはできません。個人用の追加インストールが必要なら、SessionStartフック側に足します
  • SessionStartフックの一部のパッケージマネージャーがエラーを返す: Anthropic管理環境の全outbound通信はセキュリティプロキシを経由しており、Bunなど一部のパッケージマネージャーはこのプロキシとの相性問題が公式に報告されています

よくある質問

Setup scriptとSessionStartフックを併用できますか

できます。むしろ推奨される構成です。Setup scriptで重いツールチェーンをキャッシュに焼き込み、SessionStartフックで軽量なプロジェクト依存関係のインストールを毎回走らせる、という役割分担が典型です。

Setup scriptはどのくらいの頻度で再実行されますか

環境のSetup scriptや許可ドメインを変更したとき、およびキャッシュが約7日で失効したときに再実行されます。既存セッションを再開しただけでは再実行されません。

自社ハードウェアで動かすセルフホスト環境でも同じ仕組みですか

Setup scriptとSessionStartフックという2つの仕組み自体は共通です。ただしセルフホスト環境では、運用者がRunnerホスト側から追加のフックを差し込む構成も取れる点が、Anthropic管理環境と異なります。

まとめ

VM自体の作り込みはSetup script、プロジェクトの依存関係をローカル・クラウド共通で揃えるならSessionStartフックという役割分担が基本です。Setup scriptは5分以内に完了させキャッシュに任せ、SessionStartフックは CLAUDE_CODE_REMOTE でクラウド専用の処理を切り分けます。フックの全イベント一覧を押さえておくと、SessionStart以外のタイミングでの自動化にも応用が利きます。

この記事を共有:XはてブLinkedIn