この記事の対象読者と得られること
| 対象読者 | この記事で得られること |
|---|---|
| 長時間セッションで論点が消える方 | PreCompact Hook で auto-compact を制御する手順 |
| Claude Code Hooks を深く使いたい方 | 26 種類の hook イベントのうち実務で効くものの選び方 |
| 外出先から AI に割り込みたい方 | Channels × Discord の実装イメージ |
はじめに — 「さっきの議論、どこへ行った?」問題
Claude Code で長時間の設計レビューをしていると、ある瞬間に文体がすっと変わって、会話の前半で詰めていた論点が曖昧に戻っている、という経験をした方は少なくないと思います。筆者も、2 時間ほどアーキテクチャの議論を重ねた後に「さっき結論が保留だったデータベースの選定はどうする?」と聞いたら、保留だったはずの話題が「議論済み」として要約されてしまっていて、結論がすり替わっていた、という場面に出くわしました。
これは Claude Code の auto-compact(自動会話圧縮)の副作用です。コンテキストウィンドウが埋まると、Claude Code はバックグラウンドで過去の発言を要約しながら席を空ける動きをします。この要約は便利な一方で、結論未確定の論点や、議論の途中で棚上げした課題が、要約プロセスの中で「解決済み」の体で潰されてしまうことがあるのですね。
2026 年に追加された PreCompact Hook は、まさにこの圧縮処理に割り込むためのフックです。本稿では PreCompact の挙動と settings.json の書き方、自前の要約スクリプト例、そして外出先から割り込むための Channels(研究プレビュー)まで、長時間セッションを守るための周辺技術を整理します。断定的に書いている箇所もありますが、筆者の環境で確認した範囲の話なので、運用に入れる前にご自身の手元で一度試していただくのがおすすめです。
本稿で紹介する hook 仕様は Claude Code 1.x 系(記事執筆時点の最新)を前提にしています。将来のバージョンで挙動が変わる可能性があるため、公式の Hooks Guide も併せて確認してください。
Hooks 26 種の体系と PreCompact の立ち位置
実装の話に入る前に、Claude Code Hooks の全体像を整理しておきます。そのほうが PreCompact が「どういう位置づけで登場したフックなのか」が見えやすくなると思うからです。
Claude Code v2.1 時点の hook は、大きく分けて 4 つの軸 で分類できます。Pre/Post(前後)と、Tool/Session/Compact/Prompt(対象領域)の 2 軸です。この 2 軸で切ると、26 hook events が整然と並びます。代表的なものを挙げると、次のような顔ぶれです。ツール実行を挟む PreToolUse / PostToolUse。セッション境界に関わる SessionStart / Stop / SubagentStop。ユーザー入力を加工する UserPromptSubmit。そして本稿の主役 PreCompact / PostCompact、といった具合ですね。PreCompact は「Compact 軸 × Pre フェーズ」の交差点にあります。圧縮処理を止めるか置き換えるかを決める、唯一の介入点 というわけです。
auto-compact の内部アルゴリズムも、ここで簡単に触れておきます。筆者が公式ドキュメントと実挙動から読み解いた範囲では、要約対象のメッセージに 重要度スコア を付けているようです。スコアの高い発言を残しつつ、prefix(会話の冒頭、初期指示など)と tail(直近 N ターン)は固定で保持します。中間のスコアが低い部分が優先的に要約対象になり、ここで冒頭の「結論未確定の論点が消える」現象が起きやすくなります。スコアリングのロジックは公開されておらず、あくまで観察ベースの推測です。
関連して押さえておきたいのが Session Notes との連携です。Session Notes はセッション内の約 10K tokens ごとに、自動で要点をファイルに書き出す仕組みです。auto-compact とは独立した中間保存ポイントとして機能します。PreCompact から参照すれば、直近 50K トークン分の中間要約に即アクセスできます。自前要約の材料として使えるわけですね。
さらに、v2.1 後半で研究プレビューとして入った Channels も、hook 体系と関係があります。Channels は MCP-over-network の仕組みです。MCP サーバーから実行中のセッションに、非同期で push する機能ですね。研究プレビュー段階では Discord / Telegram / iMessage / fakechat の 4 種が標準対応です(日本の読者には Discord が取っつきやすいと思います)。外部から到着したメッセージは、通常のユーザー発話と同じく UserPromptSubmit を通過します。つまり、Channels 経由の指示もローカル発話も、同じ hook パイプラインに乗る 設計なのですね。「Channels 経由のメッセージだけ追加検証する」といった実装も、hook で素直に書けます。
最後に、hook 実行順序と timeout、deny の優先度について。複数のフックが同じイベントにマッチした場合、settings.json の登録順 に実行されます。各 hook にはデフォルトで 60 秒の timeout が設定されています。超過すると exit 非ゼロ扱いになります。そして重要なのが、permissions.deny は hook の判断より 強く 効くという点です。deny に書かれたパターンは、フック側で許可しても通りません。セキュリティガードは deny にまず書く、という優先順位を意識すると事故が減ります。
Claude Code hook 機能の変遷
もうひとつ、背景として押さえておきたいのが、hook 機能そのものの歴史です。なぜ今このタイミングで PreCompact が登場したのか、という文脈が見えると、機能の狙いが理解しやすくなります。
Claude Code の v1.x 系では、そもそも /compact は 人手で発動する手動コマンド のみでした。セッションが長くなってきたらユーザーが明示的に叩いて圧縮する、という前提で、自動化機能はありません。この頃は「長時間セッションを張る」という使い方自体がまだ一般的ではなく、手動で十分だったという事情もあります。
流れが変わるのが v2.0 です。コンテキストウィンドウを 200K トークン規模で使うユーザーが増えました。「長時間タスクの途中で容量が枯渇する」問題が顕在化し、auto-compact の自動実行が導入 されます。ただしこの時点では、圧縮は完全にブラックボックスで、ユーザーが介入する手段はありません。冒頭に書いたような「結論がすり替わる」現象が、ちょうどこの v2.0 期に多く報告されるようになりました。
その対応として、v2.1 の前半で PreCompact Hook が追加されます。これは「意図消失問題」への直接的な回答と言えるもので、圧縮の停止と置き換えを hook で制御できるようになりました。ユーザーコミュニティからのフィードバックがかなり強く反映された機能、という印象です。
そして v2.1 の後半で、Channels が研究プレビュー として登場します。Discord / Telegram / iMessage / fakechat の 4 種が標準対応として用意され、セッションに割り込む経路が整いました。「長時間タスクを張りっぱなしにする」運用が、より現実的になったわけです。PreCompact と Channels が同じ v2.1 に入ったのは偶然ではなさそうです。長時間セッションを支える一対の仕組み として設計されているように見えます。
2026 年 4 月時点では、26 hook events が体系化されています。公式ドキュメントにも全イベントの一覧が載るようになりました。今後も拡張されていく見込みなので、定期的にチェックしておくと機会損失が減ります。
auto-compact の仕組みと、失われやすい情報
まず auto-compact が何をやっているのかを押さえます。Claude Code はセッションのやりとりを内部的に コンテキストウィンドウ(おおむね 200K トークン前後)という箱に積んでいて、残り容量がおよそ 20% を切ると、古い発言から順番に要約して席を空ける処理を開始します。
このとき要約に使われるのは、通常の assistant メッセージ生成と同じ Claude のモデルです。つまり「要約の品質」は、モデルに渡すプロンプトと、要約対象のコンテキストが持っている情報量で決まります。ここで問題になるのは以下の 3 点です。
- 結論が出ていない論点は、要約するときに「〜について議論した」という 1 文に縮む傾向があります
- ツール呼び出しの生ログ(大量のファイル全文など)は、要約するとファイル名だけが残って内容が消えます
- 複数ターンにまたがる意思決定プロセスは、最終的な結論だけが残り、途中で捨てた選択肢の理由が落ちがちです
結果として、長時間セッションの後半で「あの時なぜ B 案を却下したんだっけ?」と聞くと、モデルは要約された記憶だけを頼りに推測で答えることになります。これが冒頭の「結論がすり替わる」体験の正体です。
auto-compact は既定で有効になっており、残コンテキストが逼迫した段階で自動実行されます。ユーザーが /compact を明示的に叩いた場合と違い、自動実行は通知なしで走ることがあるため、長時間タスクでは「いつ要約されたか」を把握する仕組みを入れておくと安心です。
PreCompact Hook とは何か
PreCompact Hook は、Claude Code が auto-compact を実行する直前に呼び出される 26 種類ある Hook のひとつです。2026 年のリリースノートで追加され、既存の PreToolUse / PostToolUse / Stop などと同じ仕組みで settings.json に登録できます。
このフックの嬉しいところは、圧縮を止めるだけでなく、置き換えられるという点です。標準の挙動に戻す「何もしない」、圧縮そのものを拒否する「block」、自前のロジックで要約を生成して差し込む「replace」の 3 つを、終了コードと JSON の返答で使い分けます。
返り値の挙動は以下の通りです。
| 返り値 | 意味 | 主な用途 |
|---|---|---|
exit 0 |
そのまま続行。標準の auto-compact が走る | ログ取りのみ |
exit 2 |
非ゼロで止める。Claude Code 側が警告を出す | 危険タイミングでの中止 |
{"decision":"block","reason":"..."} |
構造化された block 応答 | UI に理由を出したい場合 |
{"decision":"replace","summary":"..."} |
自前の要約に差し替え | 重要論点の保全 |
decision フィールドは他の Hook とも共通の語彙です。PreToolUse で block を返すとツール実行自体が止まるように、PreCompact での block は圧縮の停止を意味します。
settings.json で PreCompact を登録する
Hook の登録はユーザースコープ(~/.claude/settings.json)か、プロジェクトスコープ(リポジトリ直下の .claude/settings.json)のいずれかで行います。チーム全体で使うなら後者、個人環境で使うなら前者という使い分けが無難です。
以下は、PreCompact で自前スクリプトを叩く最小構成の例です。ディレクトリ名は匿名化しています。
{
"hooks": {
"PreCompact": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "bash ${CLAUDE_PROJECT_DIR}/.claude/scripts/precompact-guard.sh"
}
]
}
]
}
}
matcher は空文字にしておくと全てのコンパクトイベントで発火します。特定の条件だけ(たとえば /compact を手動実行したときだけ)に絞りたい場合、将来的なフィールド追加に備えて、スクリプト側で環境変数を見て分岐する実装にしておくと後々楽でした。
settings.json の hooks フィールドは、ユーザーがローカルで settings.local.json を持っているとマージ順で上書きされます。プロジェクト配布する場合、ローカル設定の優先順位を README で明記しておくと、チームメンバーが混乱せずに済むと思います。
自前要約スクリプトの実装例
PreCompact Hook の真骨頂は、重要な論点を落とさない自前要約です。ここでは bash と jq を使って、「未解決ラベル(例: TODO:, 未決:, 保留:)が付いた行を要約から削らない」スクリプトの骨子を示します。
#!/usr/bin/env bash
# .claude/scripts/precompact-guard.sh
# PreCompact Hook から呼ばれるガードスクリプト
set -euo pipefail
# stdin から PreCompact イベントの JSON が渡ってくる
PAYLOAD="$(cat)"
# セッション履歴のパス(Claude Code が渡す)
SESSION_FILE="$(jq -r '.session_file // empty' <<<"$PAYLOAD")"
if [[ -z "$SESSION_FILE" || ! -f "$SESSION_FILE" ]]; then
# 情報が取れない場合は何もせず標準フローへ
exit 0
fi
# 未解決マーカーを含む行を抽出
UNRESOLVED=$(grep -E '(TODO:|未決:|保留:|未確定:)' "$SESSION_FILE" || true)
if [[ -z "$UNRESOLVED" ]]; then
# 未解決論点がなければ標準の auto-compact に任せる
exit 0
fi
# 未解決論点を必ず残したカスタム要約を生成
SUMMARY=$(cat <<EOF
### 未解決論点(自動保全)
$UNRESOLVED
### そのほかの経緯
※ 以降は標準要約に準拠
EOF
)
# decision=replace で Claude Code に差し戻す
jq -n --arg s "$SUMMARY" '{decision:"replace", summary:$s}'
このスクリプトは素朴な grep ベースですが、筆者の環境では「結論未確定の論点だけを確実に残す」には十分でした。もっと賢くしたい場合は、セッションファイルを LLM に渡して要約のメタ評価をさせる、という二段構えも検討できます。ただし、要約のために毎回モデル呼び出しをすると料金がかさむため、未解決ラベルが閾値を超えたときだけ LLM にかけるといった絞り込みが現実的だと思います。
jq や grep が環境に入っていない端末では即座に失敗します。CI で Claude Code を使う場合、事前に which jq が通ることを確認してから hook を有効にしてください。チーム配布ならコンテナに梱包してしまうのが確実です。
Session Notes と組み合わせる
PreCompact 単体でも強力ですが、Session Notes と組み合わせるとさらに効きます。Session Notes は Claude Code に 2026 年前半に入った仕組みで、ざっくり言うと「1 万トークンごとに要点をファイルに書き出す」機能です。
ポイントは、Session Notes が auto-compact とは独立して動く ことです。つまり、auto-compact が走る前から、10K トークンごとの小さい粒度でメモが残っているため、PreCompact Hook の中で Session Notes を参照しながら要約を組み立てられます。
# Session Notes を PreCompact で参照する例
NOTES_DIR="${CLAUDE_PROJECT_DIR}/.claude/session-notes"
LATEST_NOTES=$(ls -t "$NOTES_DIR" 2>/dev/null | head -5)
for note in $LATEST_NOTES; do
echo "### $(basename "$note")"
cat "$NOTES_DIR/$note"
done
このように、「PreCompact で最終要約の形を決める」「Session Notes で途中経過を 10K 刻みで残す」という二段構えにすると、長時間セッションでも結論が滑りにくくなります。ただし Session Notes はまだ挙動が安定していない側面もあり、筆者環境でも 3 回に 1 回くらいはファイル出力のタイミングがずれていました。運用に入れる前にログで挙動を確認しておくと安心です。
Channels(研究プレビュー) — 外から Claude Code に割り込む
ここから少し毛色が変わります。Channels は、Claude Code に外部チャットアプリから割り込むための仕組みで、記事執筆時点ではまだ研究プレビュー段階です。MCP(Model Context Protocol)サーバーを経由して、Discord / Telegram / iMessage / fakechat の 4 種から実行中のセッションにメッセージを push できます(標準対応分。Slack などは将来的に対応する可能性があります)。本記事では、日本で使い慣れている開発者が多い Discord を例に図解します。
なぜこれが PreCompact と関係するかというと、長時間タスクでは「待ち」と「割り込み」の設計が同じくらい大事だからです。ビルドに 30 分かかるタスクを回しているときに、外出先から「やっぱりデプロイ先を変えて」と指示を出せると、コンテキストスイッチがぐっと減ります。
典型的な構成は以下のようになります。
MCP サーバーの設定は .mcp.json に書きます。以下はイメージです(Channels 側の実装がまだ変わる可能性があるため、コピペより構造の理解を優先してください)。
{
"mcpServers": {
"channels": {
"command": "npx",
"args": ["-y", "@example/channels-mcp"],
"env": {
"CHANNELS_DISCORD_TOKEN": "${DISCORD_BOT_TOKEN}",
"CHANNELS_ALLOWED_USERS": "123456789012345678"
}
}
}
}
Channels は「外部から Claude Code のセッションに書き込みができる」仕組みなので、許可するユーザーやチャネルを厳密に絞らないと、意図しない第三者に権限を渡してしまう恐れがあります。CHANNELS_ALLOWED_USERS のような allowlist は必ず設定し、本番環境に入れる前にセキュリティレビューを通してください。
応用シナリオ — CI 通知と外出先 PR レビュー
Channels の使いどころとして、筆者が実際に便利だと感じたのは次の 2 つです。
CI 結果の割り込み通知。GitHub Actions の失敗通知を Discord のチャンネルに投げて、Claude Code セッション内の bot がそれを拾い、「どのテストが落ちたか」「最近の diff で該当箇所はどこか」を自動で調べ始める、という流れを組めました。筆者環境では、ローカルで長時間の設計議論をしている最中にリモートの CI が落ちたとき、セッションを切らずに原因調査に入れるのが快適でした。
外出先からの PR レビュー依頼。電車の中で GitHub の通知を見て「この PR、Claude Code にレビューさせておいて」と Discord bot に投げると、Claude Code 側で gh pr diff → 解析 → レビューコメント下書きまでを走らせておいてくれます。到着したらレビュー案が完成しているので、そのまま確認して投稿できます。
ただし、この運用には注意点もあります。外出先から投げた指示が 曖昧なまま実行されて意図と違う操作に進んでしまうことがあるのですね。筆者は「外出先モード」として、Channels 経由で来た指示は必ず確認プロンプトを出すようにしています。
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "bash ${CLAUDE_PROJECT_DIR}/.claude/scripts/channel-gate.sh"
}
]
}
]
}
}
channel-gate.sh の中で、直近の発話がどのチャネル(ローカル vs Channels)から来たかを判定し、Channels 経由なら危険コマンドを block する、という実装にしておくと、外出先の誤操作が減りました。
セキュリティ — permissions.deny と Channels の検証
ここはかなり重要なので、別立てで整理します。長時間セッション × 外部割り込みという組み合わせは便利な反面、攻撃面が広がるのも事実です。筆者が必ず入れているガード 2 つを紹介します。
ひとつめは permissions.deny の徹底です。settings.json には permissions.allow と permissions.deny の 2 つがあり、allow が空でも deny は効きます。
{
"permissions": {
"deny": [
"Bash(rm -rf *)",
"Bash(curl * | sh)",
"Bash(wget * | bash)",
"WebFetch(https://*.example-suspicious.com/*)"
]
}
}
ふたつめは Channels 経由の指示を プロンプトインジェクションの観点でサニタイズ することです。外部から来た文字列をそのままモデルのコンテキストに流すと、「これまでの指示を無視して〜」といった攻撃が刺さる可能性があります。Channels MCP サーバー側で SYSTEM: や ASSISTANT: のような制御文字列を検知したら弾く、というフィルタを入れるのが堅実です。
プロンプトインジェクションは 2026 年時点でも LLM 運用の最大リスクのひとつです。Channels のように外部メッセージを取り込む経路では、モデルに渡す前の前処理フィルタと、モデルが出した結果を実行する前の権限チェックを二重にかけるのがおすすめです。
参考 — disler/claude-code-hooks-mastery の 26 hook events 早見表
Hook の勉強をするなら、GitHub の disler/claude-code-hooks-mastery が参考になります。2026 年時点で 26 種類の hook イベントが整理されており、各イベントのペイロード例と使いどころがサンプルコード付きで載っています。以下、筆者が実務でよく触るものを抜粋します。
| Hook 名 | 発火タイミング | よくある用途 |
|---|---|---|
PreToolUse |
ツール実行直前 | 危険コマンドのブロック |
PostToolUse |
ツール実行直後 | 監査ログ記録 |
UserPromptSubmit |
ユーザー発話直後 | 秘密情報のマスキング |
PreCompact |
auto-compact 直前 | 本稿のガードスクリプト |
Stop |
セッション停止時 | 成果物の保存 |
SubagentStop |
サブエージェント終了時 | 親に結果を渡す整形 |
Notification |
通知発火時 | Channels への forward |
公式 Hook のリストは今後も拡張される可能性が高いので、定期的に hooks-guide を眺めておくと機会損失が減ります。
長文脈保存手段 4 種の比較
ここまで PreCompact Hook を中心に書いてきました。ただし、長時間セッションを守る手段は PreCompact だけではありません。近接機能と 組み合わせて使う のが現実的です。具体的には、手動 /compact、Session Notes、Auto Memory の 3 つです。それぞれの特徴を整理します。
| 軸 | PreCompact | 手動 /compact | Session Notes | Auto Memory |
|---|---|---|---|---|
| 自動化 | ○ | × | ○(10K 毎) | ○(セッション跨ぎ) |
| 介入タイミング | 圧縮直前 | 任意 | 定期 | 跨ぎ時 |
| 重要情報保持 | 設計次第 | 人判断 | 時系列 | 長期記憶 |
| 実装難度 | 中 | 低 | 低 | 低 |
| 追加コスト | ゼロ | ゼロ | ゼロ | ゼロ |
表にすると似たようなツールに見えますが、解きたい問題が微妙に違う 点に注意が必要だと思います。PreCompact は「auto-compact の品質が不満」への回答です。手動 /compact は「圧縮タイミングを自分で決めたい」への回答。Session Notes は「時系列の途中経過を残したい」への回答。Auto Memory は「セッションを跨いだ知識を持ち越したい」への回答です。このように、それぞれ守備範囲が違うわけですね。
どの手段を選ぶか
使い分けの判断マトリクスを、筆者の運用感覚でまとめます。
- PreCompact Hook を選ぶケース: 議論の途中で結論未確定が多い長時間タスクに向いています。アーキテクチャレビュー、要件の詰め、複数案の比較検討など、「後半になって前半の論点を蒸し返す」タイプのセッション では PreCompact の効果が大きいです。筆者の体感では、2 時間以上の設計セッションで効きが目に見えて出ます。
- 手動 /compact を選ぶケース: 短期セッションで、完了タイミングが明確な作業に向いています。たとえば「この関数のリファクタだけ」のような 30 分程度のタスクなら、終わりどきに自分で叩いて締める方が素直です。hook で自動化するメリットより、実装コストの方が高くなりやすいのですね。
- Session Notes を選ぶケース: 単純に時系列の保全が欲しい場合に向いています。「途中で何が決まったか」を後から振り返りたい、あるいはセッションログを別ツールに流し込みたい、という用途ではこれで十分です。
- Auto Memory を選ぶケース: セッションを跨いだ知識蓄積が目的の場合に向いています。「前回決めた方針」「先週見つけた落とし穴」をセッションまたぎで持ち越したいときは、Auto Memory が一番素直です。ただし後述する古い情報の累積には注意が必要です。
手段の組み合わせと落とし穴
組み合わせて使う場合のトレードオフも、正直に書いておきます。
ひとつめは PreCompact のデバッグが難しい という点です。非同期で、かつ発火頻度が低い(長時間セッションで 1〜2 回)ため、スクリプトのバグが出ても気づきにくいです。筆者は PreCompact スクリプトの先頭で必ず date +%s をログファイルに書き出しています。いつ発火したかを後から確認できるようにしておくと安心です。
ふたつめは Session Notes との組み合わせは冗長 という点です。両方有効にすると、10K トークンごとのノートと、圧縮時のカスタム要約が二重に残ります。情報量としては安心ですが、後からログを読み返すときに「どちらが最新か」で迷うことがあります。運用では事前にルールを決めておくと混乱が減りました。筆者は「Session Notes は参考資料、PreCompact の要約が正」と決めています。
みっつめは Auto Memory の古い情報 です。セッションを跨いで情報を貯める仕組みは便利ですが、意図的にクリーンアップしないと、古い方針が残り続けます。数ヶ月前に終わったプロジェクトの記憶が、新しいタスクのコンテキストを汚染することがあるのですね。筆者は月に一度、~/.claude/memory/ を確認して古い方針を削るメンテナンスを入れるようにしています。
よっつめは 全部併用すると context 整理のコストが逆に増える という点です。4 つ全部を有効にすると、情報源が 4 つに分散して、どれを見れば現在の状態がわかるのかが不透明になります。筆者の運用では、PreCompact + Session Notes の 2 本立てを基本 にしています。Auto Memory は副次的に使う、という構成に落ち着きました。手動 /compact は、セッションの締めで明示的に使うときだけ、という位置づけです。
どの組み合わせが最適かは、タスクの長さ・人数・機密性によって変わってきます。ご自身の環境で 1〜2 週間試してみて、手になじむ構成を探っていただくのが一番だと思います。
まとめ — 「割り込みと要約の制御」が長時間セッションの肝
本稿で紹介した内容を改めて整理します。
- auto-compact は便利ですが、未解決論点を潰す副作用があります
- PreCompact Hook は
exit 2/decision:block/decision:replaceで圧縮を制御できます - 自前要約スクリプトで未解決マーカーを保全すれば、結論のすり替わりを大きく減らせます
- Session Notes と併用すると、10K トークンごとの小粒度メモと最終要約の二段構えになります
- Channels で外部からの割り込みを受けるなら、allowlist とプロンプトインジェクション対策は必須です
-
permissions.denyは allow より強く効くので、危険コマンドはまずここに書きます
長時間タスクの AI 運用は、まだベストプラクティスが固まりきっていない領域だと感じています。本稿の内容もあくまで筆者の手元で有効だった構成なので、環境や目的に応じて調整していただければと思います。もしもっと良い構成を見つけた方がいらっしゃれば、コメントで教えていただけると嬉しいです。