Claude Media
CLAUDE_JOB_DIRでバックグラウンドセッションのスクラッチ領域を使う

CLAUDE_JOB_DIRでバックグラウンドセッションのスクラッチ領域を使う

バックグラウンドセッションに設定されるCLAUDE_JOB_DIRの中身と、$CLAUDE_JOB_DIR/tmpへの書き込みが権限プロンプトを出さない仕組みを解説します。

このTipsでできること

Claude Codeのバックグラウンドセッションには、CLAUDE_JOB_DIRという環境変数が自動で設定されます。このセッションが実行するシェルコマンドはこの変数を引き継ぎ、$CLAUDE_JOB_DIR/tmpにファイルを書けば権限プロンプトなしで済みます。仕組みを知っておくと、並列で動かす複数のバックグラウンドセッションが一時ファイルで衝突する事故を避けられます。

Claude Codeのバックグラウンド実行機能は、claude --bgやagent viewから起動したセッションを、ターミナルを閉じても動かし続ける仕組みです。claude agents --jsonでバックグラウンドセッションを操作するでは一覧取得やスクリプト連携を扱いましたが、本稿はそのセッションが内部で使うディレクトリと環境変数に絞って掘り下げます。

CLAUDE_JOB_DIRとは何か

Claude Codeはagent viewからセッションをバックグラウンドで動かすとき、そのセッションを~/.claude/jobs/<id>ディレクトリに紐づけます。CLAUDE_JOB_DIRはこのパスをそのまま値に持つ環境変数で、セッション内で実行するシェルコマンドはすべてこれを引き継ぎます。

セッションIDはagent view上の1行に対応する識別子です。claude agents --jsonで一覧を取得すると、稼働中の各セッションのIDと状態がJSONで返ります。IDが分かれば~/.claude/jobs/<id>のパスも一意に決まります。

対話モードで直接claudeを起動したセッションではこの変数は設定されません。存在するかどうかは、そのプロセスがagent view管理下のバックグラウンドジョブかどうかの目印にもなります。/statusコマンドを実行するとSession kind欄にbackground job · attachedやbackground job · unattendedと表示され、対話セッションではinteractiveとだけ表示されます。バックグラウンドかどうかを判定する材料はCLAUDE_JOB_DIRの有無と/statusの両方があることになります。

やり方 — $CLAUDE_JOB_DIR/tmpにスクラッチファイルを書く

一時ファイルの置き場所は$CLAUDE_JOB_DIR直下ではなく、その下のtmpサブディレクトリです。ここへのWrite / Edit呼び出しは権限プロンプトを出さずに実行されます。

echo "$CLAUDE_JOB_DIR"
# => /Users/you/.claude/jobs/a1b2c3d4
 
ls "$CLAUDE_JOB_DIR/tmp"

バックグラウンドセッションからClaudeがシェルコマンドを実行する際、このコマンドはCLAUDE_JOB_DIRを継承した環境で動きます。スクリプト側で$CLAUDE_JOB_DIR/tmpをワークディレクトリとして指定すれば、複数のセッションが同時に走っていても一時ファイルのパスが衝突しません。プロジェクトの作業ディレクトリに直接一時ファイルを書くと、並列セッション同士が同じファイル名を取り合う可能性がありますが、$CLAUDE_JOB_DIR/tmpはセッションごとに固有なのでその心配がありません。

セッション削除で消える仕組み

~/.claude/jobs/<id>ディレクトリは、対応するセッションを削除すると同時に消えます。長時間置いておく成果物ではなく、あくまでそのセッションが生きている間だけの作業領域という位置づけです。

バックグラウンド実行に関わる状態は~/.claude配下に複数の形で保存されています。

パス内容
~/.claude/daemon.log内容supervisor(バックグラウンドセッションを動かし続ける常駐プロセス)のログ
~/.claude/daemon/roster.json内容稼働中バックグラウンドセッションの一覧。再起動後の再接続に使う
~/.claude/jobs/<id>/state.json内容agent viewに表示されるセッションごとの状態。直接パースせずclaude agents --json経由で読む
~/.claude/jobs/<id>/tmp/内容セッションごとのスクラッチ領域。Write / Editが権限プロンプトなしで動く。セッション削除で消える

CLAUDE_CONFIG_DIRを設定している場合、supervisorは~/.claudeの代わりにそのディレクトリを使い、独立したインスタンスとして別のセッション群を管理します。

稼働状況を直接ファイルから追わずに確認したいときはclaude daemon statusが使えます。supervisorに到達できるか、そのプロセスIDとバージョン、稼働中セッション数を返します。

supervisorとセッションプロセスの関係

バックグラウンドセッションを裏で動かし続けているのはsupervisorという常駐プロセスです。agent viewを開くか、最初にセッションをバックグラウンドに送った時点で自動的に起動し、個別に管理する必要はありません。各セッションはsupervisor配下の独立したClaude Codeプロセスとして動き、状態によって扱いが変わります。

  • 作業中、権限プロンプト待ち、または画面にアタッチ中: プロセスは動き続けます
  • 完了または次の入力待ちのまま、未アタッチで約1時間経過: supervisorがプロセスを止めてリソースを解放します。会話自体はディスクに残るので、次にアタッチするか返信すれば続きから再開します。Ctrl+Tでピン留めすればプロセスは止まりません
  • supervisor稼働中に予期せず終了した場合: supervisorがプロセスを再起動します
  • 自動アップデート後: supervisorは新バージョンへ自ら再起動し、アイドル状態のセッションを裏で引き継ぎます

セッションのプロセスが止まったり再起動したりしても、そのセッションが開始したバックグラウンドのシェルコマンドやサブエージェントは次のプロセスに引き継がれます。この引き継ぎを止めたい場合は環境変数CLAUDE_CODE_DISABLE_BG_EXIT_HANDOFFを1に設定します。セッションを削除すれば、引き継がれていたものもまとめて止まります。

権限プロンプトが必要なケースとの違い(落とし穴)

$CLAUDE_JOB_DIR/tmpへの書き込みが無条件に許可される一方で、プロジェクトの作業ディレクトリ内のファイル編集は別の仕組みで守られています。バックグラウンドセッションはファイルを編集する前に、.claude/worktrees/配下の専用git worktreeへ自動的に移動し、以降はセッションとそのサブエージェントの書き込みがそのworktreeに隔離されます。この隔離は、セッションが既にworktree内にいる場合、編集対象のファイルが既に別のworktree内にある場合、作業ディレクトリがgitリポジトリでなくWorktreeCreateフックも設定されていない場合、書き込み先が作業ディレクトリの外にある場合はスキップされます。

つまり、並列セッションの衝突を避ける仕組みは2種類あり、性質が異なります。

  • $CLAUDE_JOB_DIR/tmp: セッション固有のスクラッチ領域。プロジェクトのファイルではないので、そもそも権限確認の対象外
  • worktree内のプロジェクトファイル: 権限確認自体は必要だが、隔離によって他セッションとの衝突を防ぐ

additionalDirectoriesは権限だけ拡張するで扱った設定も、この2種類とはさらに別の軸です。additionalDirectoriesはファイルアクセスの許可範囲を広げるだけで、権限プロンプト自体を省略するものではありません。対して$CLAUDE_JOB_DIR/tmpはプロンプトの省略そのものが仕組みに組み込まれています。承認フローを外部に中継したい場合は権限確認プロンプトをChannelsで中継する実装手順のような別の設計と組み合わせることになります。

worktreeの後始末は、通常はセッション削除と同時に自動で行われます。agent viewでセッションを削除すると、安全に削除できる場合はそのセッション用worktreeも一緒に消えますが、削除の仕方によってはworktreeやそのディレクトリが残ることがあり、.claude/worktrees/が肥大化する原因になります。残ったエントリはgit worktree listで確認し、git worktree remove <path>で個別に消す必要があります。$CLAUDE_JOB_DIR/tmpはセッション削除で確実に消える点が、この点でもworktreeと対照的です。

具体例 — フックのデバッグログを$CLAUDE_JOB_DIR/tmpに書く

複数のバックグラウンドセッションを並列で走らせながらフックのデバッグをしている場合、ログファイルの置き場所をプロジェクト直下に固定すると、どのセッションが書いたログか分からなくなります。$CLAUDE_JOB_DIRが空でなければバックグラウンドセッションだと判定し、ログをそのセッション専用のtmp配下に振り分ける、という条件分岐が使えます。

if [ -n "$CLAUDE_JOB_DIR" ]; then
  log_dir="$CLAUDE_JOB_DIR/tmp"
else
  log_dir="/tmp/claude-hook-logs"
fi
mkdir -p "$log_dir"
echo "$(date) hook fired" >> "$log_dir/hook.log"

この方法なら、対話セッションで動かしたときは従来どおり/tmp配下に書き、バックグラウンドセッションで動かしたときはセッションごとに独立したパスへ書き分けられます。ログの置き場所を環境変数の有無で切り替えるだけなので、フック側のコード自体を変更する必要はありません。

バックグラウンドセッションはローカルマシン上で動く点も押さえておく必要があります。並列に10セッション動かせば、サブスクリプションの使用量もインタラクティブセッション10個ぶんと同じ速度で消費されます。マシンをスリープさせても保持されますが、シャットダウンすれば止まります。$CLAUDE_JOB_DIRが指すディレクトリもローカルディスク上にあるため、マシンをまたいで共有するスクラッチ領域としては使えません。

セッション削除後もアクセスできるもの

claude rm <id>でセッションを削除すると、そのセッション用の~/.claude/jobs/<id>($CLAUDE_JOB_DIRが指す場所)は、worktreeも含めて安全に削除できる範囲で片付けられます。ただし会話のトランスクリプト自体はローカルマシンに残り、claude --resumeから引き続き参照できます。$CLAUDE_JOB_DIR/tmpに書いたスクラッチファイルが消えることと、会話そのものが消えることは別の話です。

セッションの直近の出力をシェルからすぐ確認したいときはclaude logs <id>が使えます。$CLAUDE_JOB_DIR/tmpのようにディスク上に残り続けるスクラッチ領域ではなく、あくまでその時点の出力を表示するコマンドなので、後から見返す用途には向きません。

まとめ

CLAUDE_JOB_DIRはagent view経由のバックグラウンドセッションに自動で設定される環境変数で、~/.claude/jobs/<id>を指します。$CLAUDE_JOB_DIR/tmpへのWrite / Editは権限プロンプトなしで実行され、セッション削除と同時にディレクトリごと消えます。並列でバックグラウンドセッションを走らせるスクリプトを書くなら、一時ファイルの置き場所として$CLAUDE_JOB_DIR/tmpを使うと衝突を避けられます。プロジェクトファイルの隔離はworktreeが別途担っており、両者は仕組みも消え方も異なることを押さえておくと、バックグラウンド運用時の設計判断がしやすくなります。

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