目次

目次
はじめに
AI Integration Unit の山崎です。
日々、業務はもちろんプライベートでもClaude Codeを活用して開発や検証、ドキュメントの作成などを行っています。こうして毎日使い込むうちに、AIとの向き合い方について自分なりの考えが固まってきました。それは、「AIを伴走者と捉え、ともに考え議論する」というものです。(AI と共創する開発文化 – AI ファーストな開発スタイルの先へでも、AIを伴走者として使うということが紹介されています。)コードの作成こそAIに任せるようになりましたが、そこに至るまでの要件整理や設計段階では、人間が考えた要件やロジックを伝えながらAIと共に思考を深めていきます。タスクを丸投げする相手ではないのです。
本記事では、その伴走者との議論や判断の過程を残すために作成した handover スキルを紹介します。Claude Codeのカスタムスラッシュコマンド(/handover)でセッション引き継ぎノートを生成し、フックやCLAUDE.mdと組み合わせることで、特別な操作をしなくても文脈がセッションをまたいで引き継がれる仕組みです。実際に運用しているものをGitHubリポジトリで公開しているので、そのREADMEに沿って構成や考え方を解説します。
背景
伴走者との議論をいくら深めても、その過程が残らなければあとから振り返ることができません。「なぜその判断をしたのか」「どのような思考過程を経てその結論に至ったのか」「ほかの選択肢を採用しなかった理由はなにか」が失われると、同じ課題に直面したときに議論を一からやり直すことになり、せっかく積み上げた判断の再現性が損なわれます。さらに、作業を中断したときに「次になにをすべきか」が残っていなければ、再開時に状況の把握から始めなければなりません。
この問題は、Claude Codeのセッションの仕組みによって増幅されます。Claude Codeはセッションを終了すると、それまでの会話内容がリセットされてしまうためです。--resumeオプションをつけて起動したり、セッション中に/resumeコマンドを実行することで過去の会話を呼び出せますが、それでも次の課題が残ります。
- 「どのような作業を行ったか」「次にやるべきことはなにか」を知るには、会話履歴を読み直すかClaude Codeに尋ねるしかない
- 「なぜその判断をしたのか / しなかったのか」という背景がプロジェクトの記録として残らない
- デフォルトでは、30日以上非アクティブなセッションはClaude Code起動時に削除されてしまう
そこで以前は「ここまでの作業をノートに残して」といった簡単な指示で対処していました。しかし、その都度の指示では生成されるノートの粒度に差が出たり、フォーマットがそろわなかったりと、あとから読み返す資料としては使い勝手がよくありませんでした。判断の過程を一定の品質で確実に残すには、出力の構造をあらかじめ定義しておく必要があります。これが handover スキルを用意した動機です。
handoverスキルは、さまざまな形のものがネット上で公開されています。今回は以下の記事を参考にしながら、自分の運用に合った形に作り込みました。
引き継ぎノートの例
まず、このスキルが生成するノートの実物を示します。/handover を実行すると、プロジェクトルートの .claude/handovers/ に YYYY-MM-DD_HHmm.md 形式のノートが追記されていきます。1ファイルが1セッションに対応します。
.claude/handovers/ ├── 2026-03-08_2130.md ├── 2026-03-07_1845.md └── 2026-03-06_1200.md
中身は次のような構造です。各セクションが、次に再開する人(あしたの自分)が必要とする観点に対応しています。
# セッション引き継ぎノート 日時: 2026-03-08 21:30 状態: Working(main 未マージ、レビュー前) 駆動: Issue #1「セッション引き継ぎ自動化」 ## リポジトリ状態 - ブランチ: `feat/handover-skill`(main から分岐、未マージ) - 未コミット: `skills/handover/SKILL.md`(新規)、`hooks/stop-handover-reminder.sh`(新規) - 未プッシュ: 2 コミット(abc1234..def5678) - テスト: 未実行 ## 最初に読む(cold-read順) 1. `skills/handover/SKILL.md` — スキル本体の現状と設計意図(≈5分) 2. `~/.claude/settings.json` — Stop フックの登録状況(≈2分) 3. `hooks/stop-handover-reminder.sh` — 自動起動ロジック(≈3分) ## 今回やったこと - handover スキルと自動化フックを設計・実装した(Issue #1「セッション引き継ぎ自動化」への対応) - handover スキルの SKILL.md を新規作成した - PreCompact フックを試作したが、compaction 中は tool 使用が制限されファイルを書けないと判明し不採用にした - claude-code-skills リポジトリを新規作成し、install.sh で symlink 管理に移行した - stop-handover-reminder.sh を追加し、コンテキスト 70% 時点で自動的に handover を起動する仕組みに切り替えた ## 決定事項 - handover は Skill 形式で管理する。Plugin Agent 形式は採用しない - 自動起動は Stop フック(閾値 + フラグファイル)のみで行う。PreCompact フックは tool 使用が制限され実効がないため採用しない ## 捨てた選択肢と理由 - Plugin Agent 方式: sub-agent は親の会話履歴にアクセスできないため引き継ぎ品質が落ちる — Skill(inline 実行)方式を採用 - PreCompact フックをメインにする案: compaction 中は tool 使用が制限されファイルを書けない — Stop フック(閾値 + フラグ)に変更 ## ハマりどころ - PreCompact フックが systemMessage を注入しても Claude がファイルを書かない → compaction 中は tool 使用が制限される仕様のため → Stop フック方式に切り替えて解決 ## 学び - Stop フックで decision に block を返すと、Claude がそのメッセージを指示として受け取り実行する - Stop フック入力の stop_hook_active が true のときはスキップしないと、decision:block 後に無限ループになる ## 次にやること - [起点] git status で未コミットファイルを確認してから PR マージに進む - [必須] GitHub にリモートリポジトリを作成して push する - [任意] 他に管理すべき skill や hook が生まれた場合は随時追加 ## 関連ファイル - ~/.claude/skills/handover/SKILL.md(handover スキル本体) - ~/.claude/hooks/stop-handover-reminder.sh(Stop フック) - ~/.claude/settings.json(フック登録) ## 関連メモリ - [[handover-skill-design]] — handover を Skill 形式にした設計判断の経緯
このテンプレートで私が特に重視しているのが「捨てた選択肢と理由」と「最初に読む(cold-read順)」の2つです。
前者は、先述のとおり「ほかの選択肢を採用しなかった理由」をそのまま残す欄です。これがあると、次のセッションで一度却下したはずの案を再検討して堂々巡りになることを防げます。後者は、会話履歴を持たない次のセッションに対して「どのファイルをどの順で読めば文脈を復元できるか」を示す道案内で、読む順と所要時間の目安まで添えます。スキル本体にも「この文脈を知らない人が読んで迷わず再開できるか」を執筆基準として明記しており、ノートの読み手を未来の他人と想定している点が、単なる作業ログとの違いです。
カスタムスラッシュコマンドの構成
handover は Claude Code の Skill として実装しています。実体は skills/handover/SKILL.md 1ファイルで、先頭の frontmatter とそれに続く本文から成ります。
--- name: handover description: Use when the user runs /handover, when a session is ending, when context compaction is about to happen, or when the user asks to generate a session summary, handover note, or 引き継ぎノート. ... ---
SKILL.md の全文を見る
---
name: handover
description: Use when the user runs /handover, when a session is ending, when context compaction is about to happen, or when the user asks to generate a session summary, handover note, or 引き継ぎノート. Also use when context is running low and key decisions should be preserved before they are lost.
---
# セッション引き継ぎノート生成
このノートの読み手は、会話履歴を持たない次のClaudeセッション(または翌日の開発者自身)。
「この文脈を知らない人が読んで迷わず再開できるか」を基準に書く。
## Process
1. タイムスタンプ取得: `date '+%Y-%m-%d_%H%M'`
2. ディレクトリ確保: プロジェクトルートの `.claude/handovers/` を使用。プロジェクト外なら `~/.claude/handovers/`。`mkdir -p` で作成。
3. ファイル名決定: `YYYY-MM-DD_HHmm.md`。同名が存在する場合は `_2`, `_3` を末尾に付与。
4. 会話全体を振り返り、以下を意識的に確認する:
- 明示・暗示を問わず却下されたアプローチ(Claudeが提案してユーザーに否定されたものも含む)
- セッション序盤に行われた設計判断(長いセッションでは忘れがちなため)
- エラーや詰まりがあったポイントとその解消方法
- 現在のgit状態(ブランチ名、未コミット・未プッシュのファイル、テスト実行結果)
- cold-read順(次セッションが最初に読むべきファイルを、読む順に3〜5件)
- 駆動している Issue/PR と、関連する memory エントリ
5. 以下のテンプレートで引き継ぎノートを生成する。
## Quality Rules
- 事実ベースで書く。推測・曖昧な表現は禁止。
- 「捨てた選択肢と理由」は最重要セクション。次回同じ議論を繰り返さないために具体的に。
- 各セクションは原則必須。該当なしの場合は「なし」と記載(「関連メモリ」など任意明記のセクションは省略可)。
- 簡潔に、箇条書き中心。
- セクションごとの具体的な書き方ルールは、下の記入例の各見出し直下のコメントに記載。
## 記入例(このまま雛形として使う)
以下はそのまま雛形として複製でき、同時に良い記入例でもある。各セクション見出し直下のコメント(`<!-- -->`)が書き方のルール。複製して実ノートを書く際はコメント行を削除する。
```markdown
# セッション引き継ぎノート
日時: 2026-03-17 17:26
<!-- 状態: Working/レビュー前/ブロック中/完了 等。駆動: このセッションが対応する Issue/PR を1行(無ければ「なし」) -->
状態: Working(main 未マージ、レビュー前)
駆動: Issue #1「セッション引き継ぎ自動化」
## リポジトリ状態
<!-- ブランチ/未コミット/未プッシュ/テスト結果。変更なし・クリーンなら「なし(main、クリーン)」の1行で -->
- ブランチ: `feat/handover-skill`(main から分岐、未マージ)
- 未コミット: `skills/handover/SKILL.md`(新規)、`hooks/stop-handover-reminder.sh`(新規)
- 未プッシュ: 2 コミット(abc1234..def5678)
- テスト: 未実行
## 最初に読む(cold-read順)
<!-- 予備知識ゼロで再開する人がこの順に読めば文脈を復元できるファイルを3〜5件。所要時間の目安を添える -->
1. `skills/handover/SKILL.md` — スキル本体の現状と設計意図(≈5分)
2. `~/.claude/settings.json` — Stop フックの登録状況(≈2分)
3. `hooks/stop-handover-reminder.sh` — 自動起動ロジック(≈3分)
## 今回やったこと
<!-- 1行目に目的・背景を書く。以降は動詞+対象+状態の完了形で。体言止めは使わない(「認証機能の実装」ではなく「JWT認証ミドルウェアを実装した」) -->
- handover スキルと自動化フックの設計・実装(Issue #1「セッション引き継ぎ自動化」への対応)
- handoverスキルのSKILL.mdを新規作成した(~/.claude/skills/handover/SKILL.md)
- PreCompactフックを試作したが、compaction中はtool使用が制限されファイルを書けないと判明し不採用にした(→「捨てた選択肢」参照)
- Plugin Agent方式での実装を検討したが断念した(→「捨てた選択肢」参照)
- claude-code-skillsリポジトリを新規作成し、install.shでsymlink管理に移行した
- stop-handover-reminder.shフックを追加し、トランスクリプト末尾のusageから算出した文脈使用率が70%を超えた時点でhandoverを促す仕組みにした
## 決定事項
<!-- 判断+理由を1行で -->
- handoverはSkill形式で管理する。Plugin Agent形式は採用しない
- 自動起動はStopフック(文脈使用率70%の閾値+フラグファイル)のみで行う。PreCompactフックはtool使用が制限され実効がないため採用しない
- hookのsystemMessageは指示注入方式(claude CLIを直接呼ぶ方式はタイムアウト・再帰リスクあり)
- Stopフックの「頻度が高い」問題は文脈使用率の閾値とフラグファイルで解決できる(1セッション1回のみ発火)
## 捨てた選択肢と理由
<!-- [選択肢名]: [却下した理由] — [代わりに採用したアプローチ]。Claudeが提案しユーザーに否定された案も記録する。最重要セクション -->
- Plugin Agent方式: sub-agentは親の会話履歴にアクセスできないため引き継ぎ品質が落ちる — Skill(inline実行)方式を採用
- Stopフック(閾値なし): 全レスポンス後に発火しすぎる — 閾値(文脈使用率70%)+フラグファイルで1セッション1回に制限して採用
- トランスクリプトのバイト数で判定する案: バイト数は文脈使用率と相関せず誤発火する(ツール多用で早すぎ/軽量セッションでは未発火)— 末尾usageのトークン数(input+cache_read+cache_creation)から文脈使用率を算出する方式に変更
- PreCompactフックをメインにする案: compaction中はtool使用が制限されファイルを書けない — Stopフック(閾値+フラグ)に変更
- claude CLIをhookから直接呼ぶ方式: 再帰呼び出しリスクとAPIコスト・タイムアウトリスクがある — systemMessage注入方式を採用
## ハマりどころ
<!-- 現象 → 原因 → 解消方法の3点セットで書く -->
- PreCompactフックがsystemMessageを注入してもClaudeがファイルを書かない → compaction中はtool使用が制限される仕様のため → Stopフック方式に切り替えて解決
## 学び
<!-- このセッション固有の非自明な知見のみ。一般論(「型安全性は重要」など)は書かない -->
- Claude CodeのskillsとPlugin Agentsはfrontmatterフィールドが異なる(skills: description/matchのみ、agents: さらに多くのフィールド)
- PreCompactフックのsystemMessageはJSON形式で出力しないとClaude Codeに無視される
- PreCompactフックのsystemMessageはClaudeに届くが、compaction中はtool使用が制限されるためファイル書き込み不可
- Stopフックでdecisionにblockを返すとClaudeがそのメッセージを受け取り指示として実行する
- Stopフック入力のstop_hook_activeがtrueのときはスキップしないとdecision:block後に無限ループになる
- Stopフック入力には文脈使用率が含まれないため、トランスクリプト末尾のusage(input+cache_read+cache_creation)からlive文脈のトークン数を算出する。文脈ウィンドウはmodel名から推定する(Opus 4.x=1M、Sonnet/Haiku=200K)。判別不能なSonnet 4.6 1M版や任意サイズはCONTEXT_WINDOW_OVERRIDEで上書きする
- symlinkでディレクトリを張る場合、`ln -sf {dir}/` とスラッシュ付きで指定しないとシンボリックリンク自体がリンクされる
## 次にやること
<!-- [起点]最初に実行すべき確認・コマンド、[必須]必ずやること、[任意]いつかやること(なければ省略可) -->
- [起点] git status で未コミットファイルを確認してからPRマージに進む
- [必須] GitHubにリモートリポジトリを作成してpushする
- [任意] 他に管理すべきskillやhookが生まれた場合は随時追加
## 関連ファイル
<!-- パス+一言説明 -->
- ~/.claude/skills/handover/SKILL.md(handoverスキル本体)
- ~/.claude/hooks/stop-handover-reminder.sh(Stopフック)
- ~/.claude/settings.json(フック登録)
- ~/go/src/github.com/revsystem/claude-code-skills/install.sh(symlink管理スクリプト)
## 関連メモリ
<!-- このセッションに関連する ~/.claude/.../memory/ のエントリ名。無ければ省略可 -->
- [[handover-skill-design]] — handover を Skill 形式にした設計判断の経緯
```
description は、このスキルをいつ起動すべきかを Claude に伝える条件文です。/handover という明示的な実行のほか、「引き継ぎノートを生成して」といった自然言語や、セッション終了・コンテキスト圧縮が近いといった状況でも起動するように記述しています。後述するフックは、この description が想定する状況を実際に作り出して自動起動につなげます。
本文は3部構成です。Process でタイムスタンプ取得からファイル名決定、会話の振り返りまでの手順を定義し、Quality Rules で「事実ベースで書く」「捨てた選択肢と理由を最重要セクションとする」といった品質基準を示します。そして 記入例 に、コメント付きの雛形をそのまま置いています。各セクション見出しの直下に <!-- --> で書き方のルールを添えてあるため、Claude はこの雛形を複製してコメントを消すだけで、粒度とフォーマットのそろったノートを書けます。先述のとおり「その都度の指示では品質がそろわない」という問題を、出力構造をスキル側に固定することで解消しているわけです。
設計上の最大の判断は、これを Plugin Agent(サブエージェント)ではなく Skill としてインライン実行する点にあります。サブエージェントは親セッションの会話履歴にアクセスできないため、引き継ぎノートの肝心の中身を再現できません。インライン実行であれば、いままさに進めてきた会話そのものを振り返ってノートを書けます。
スキルのインストールは gh skill install コマンドで行います。(GitHub CopilotやCodexを使っている場合は、–agent引数にそのエージェント名を指定してください。詳細は公式ドキュメント gh skill install を参照してください。)
gh skill install revsystem/claude-code-skills handover --agent claude-code --scope user
gh が使えない環境であれば、上記の SKILL.md の内容をコピーし、~/.claude/skills/handover/ 以下に直接配置しても同じように動きます。
自動化のためのフック
/handover を手で叩くだけでも使えますが、それでは「書こうと思ったときには会話が終わっていた」「コンテキストが圧縮されて記憶が薄れた後だった」という取りこぼしが起きます。そこで Stopフックで自動起動を補います。
自動起動は Stopフック(stop-handover-reminder.sh)が担います。Claude のレスポンスが終わるたびに発火し、現在のコンテキスト使用率を見て、閾値(既定 70%)を超えていたら /handover の実行を促します。促し方は、フックの出力で decision: block を返し、その reason に指示文を載せる方式です。Claude はこの reason を指示として受け取り、その場で handover スキルを起動します。1セッションに何度も発火しないよう、初回に /tmp 配下のフラグファイルを作成し、以降はスキップします。
ここで問題になるのが「コンテキスト使用率をどう測るか」です。当初はトランスクリプトファイルのバイト数(1MB)を閾値にしていましたが、これは実際の使用率と相関しません。ツールを多用するセッションでは早すぎるタイミングで発火し、逆に軽量なセッションでは 90% 近くでも発火しないまま圧縮されてしまうことがありました。
そこで判定をトークンベースに改めました。Stopフックの入力には使用率そのものが含まれないため、トランスクリプト末尾の assistant メッセージが記録する usage(input_tokens + cache_read_input_tokens + cache_creation_input_tokens)を合算し、その値を現在の live コンテキストのトークン数として扱います。コンテキストウィンドウのサイズはモデル名から推定し(Opus 4.x なら 1M、それ以外は 200K)、自動判別できない Sonnet 4.6 の 1M 版と通常版などのために CONTEXT_WINDOW_OVERRIDE で手設定もできます。
この仕組みは最初から Stopフックに決め打ちしていたわけではなく、試行錯誤の産物です。実は Claude Code には、コンテキスト圧縮の直前に発火する PreCompact というフックもあります。圧縮で記憶が薄れる寸前にノートを書かせれば取りこぼしがない、と考えて当初はこちらを主役に据えるつもりでした。しかし圧縮処理中は tool 使用が制限され、Claude はファイルを書けません。ノートが生成されないまま会話が要約されてしまうため、この案は不採用にしました。こうして残った「試したが捨てた選択肢」こそ、まさに handover スキルがノートに書き残す対象です。実際この判断も引き継ぎノートに記録され、後から同じ袋小路に入り込まずに済みました。スキルが自分自身の開発を支える、という構図になっています。
Stopフックは ~/.claude/settings.json に登録します。フックは gh skill install では登録できないため、手動で追記します。
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "bash ~/.claude/hooks/stop-handover-reminder.sh",
"timeout": 5
}
]
}
]
}
}
フックスクリプトの全文は以下のとおりです。~/.claude/hooks/ 以下に配置します。
stop-handover-reminder.sh(Stopフック)の全文を見る
#!/usr/bin/env bash
set -euo pipefail
# === 設定 ===========================================================
# 文脈の何%で引き継ぎ(/handover)を促すか。
TRIGGER_PCT=70
# 文脈ウィンドウのトークン数。空のときは下部で model 名から自動推定する
# (Opus 4.x=1M、Sonnet/Haiku=200K)。Sonnet 4.6 は 200K版/1M版で model 名が
# 同一で自動判別できないため、1M版を使う場合はここに明示する(例: 1000000)。
# モデルに関わらず任意の文脈サイズを固定したい場合もここに値を入れる。
CONTEXT_WINDOW_OVERRIDE=""
# ====================================================================
INPUT=$(cat)
# stop_hook_active=true のときは無限ループを防ぐためスキップ
STOP_HOOK_ACTIVE=$(echo "$INPUT" | jq -r '.stop_hook_active // false')
if [ "$STOP_HOOK_ACTIVE" = "true" ]; then
exit 0
fi
SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // "unknown"')
TRANSCRIPT=$(echo "$INPUT" | jq -r '.transcript_path // ""')
# 1セッションにつき1回だけ発火するフラグファイル
FLAG_FILE="/tmp/claude-handover-triggered-${SESSION_ID}"
if [ -f "$FLAG_FILE" ]; then
exit 0
fi
# トランスクリプトファイルが存在しない場合はスキップ
if [ ! -f "$TRANSCRIPT" ]; then
exit 0
fi
# --- live コンテキストのトークン数を算出 ---------------------------------
# Stop フックの入力には文脈使用率が含まれないため、トランスクリプト末尾の
# assistant メッセージが記録する usage から live コンテキスト相当を求める。
# input + cache_read + cache_creation は、その turn のプロンプト規模であり
# 現在の文脈トークン数にほぼ一致する。
USAGE_TSV=$(jq -s -rc '
(map(select(.message.usage? != null)) | last) as $m
| if $m == null then empty
else [ (($m.message.usage.input_tokens // 0)
+ ($m.message.usage.cache_read_input_tokens // 0)
+ ($m.message.usage.cache_creation_input_tokens // 0)),
($m.message.model // "unknown") ] | @tsv
end
' "$TRANSCRIPT" 2>/dev/null || true)
# usage が取れなければ判定不能としてスキップ(誤発火しない方に倒す)
if [ -z "$USAGE_TSV" ]; then
exit 0
fi
IFS=
CLAUDE.md への追記でノートを自動読み込み
ノートを生成しても、次のセッションがそれを読まなければ意味がありません。読み込みは CLAUDE.md への指示で閉じます。~/.claude/CLAUDE.md に次の3行を追加しておくと、セッション開始時に Claude が自動で最新のノートを確認します。
# セッション引き継ぎ - セッション開始時にプロジェクトルートの `.claude/handovers/` ディレクトリを確認し、ファイルが存在すれば最新のものを読み込む - コンテキスト使用率が高くなると Stop フックが `/handover` の実行を促す - 任意のタイミングで `/handover` を実行すれば手動でも引き継ぎノートを生成できる
これで「コンテキストが膨らんだら自動でノートを書き(Stopフック)、セッションをまたいでも次回開始時に読み込む(CLAUDE.md)」という一連の流れが、手を動かさずに回るようになります。
付録 非アクティブなセッションの削除対策
先述のとおり、デフォルトでは30日以上非アクティブなセッションがClaude Code起動時に削除されます。引き継ぎノートを .claude/handovers/ に残しておけば会話の要点は失われませんが、セッションそのもの(--resume で開ける完全な会話履歴)も長く残したい場合は、~/.claude/settings.json に以下のようにcleanupPeriodDays(単位は日)を追記して保持期間を延ばせます。詳細は、公式ドキュメントClaude Codeの設定を参照してください。
{
"cleanupPeriodDays": 9999
}
まとめ
Claude Code のセッションは終了すれば失われますが、handover スキルとフック、CLAUDE.md を組み合わせることで、議論や判断の過程をノートとして残し、特別な操作をしなくても次のセッションへ引き継げるようになりました。
とりわけ「なぜその選択肢を捨てたのか」を書き残せることが、私にとっては大きな価値でした。判断の理由が記録に残ることで、議論は使い捨てにならず、回を重ねるごとに前進します。
ここで紹介した構成はGitHubリポジトリで公開しています。そのまま使うこともできますし、記入項目やフックの閾値をご自身の運用に合わせて調整する出発点にしていただければと思います。伴走者であるAIとの記録を残す仕組みは、思考を一度きりで終わらせないための有益な仕組みになると期待しています。
注: 本記事で紹介したコードは、記事掲載当初のものです。最新版は GitHub リポジトリ を参照してください。