SLOPSHOPPER

context-handoff

Shows context/rate-limit usage in the status line and checkpoints work for the next session when a limit gets close

newbandcommandtoaststatusmodel
★ 1v0.1.0MITupdated 2026-10-04Wanyaldee/wanyaldee-skills/mods/context-handoff
A shopper browsing a rack in a slop shop
Preview · a replayed session in a sandbox
claude · ~/work/app · context-handoff
› fix the failing auth test and add an audit log call ╭────────────────────────────╮ │ context-handoff │ ⏺ Read(src/auth.ts) │ 手動で引き継ぎを要求。作業を記録してコミットします… │ ⎿ Read 6 lines ╰────────────────────────────╯ ⏺ Update(src/auth.ts) ╭─────────────────────────────────────╮ ⎿ Added 2 lines, removed 1 line │ context-handoff │ ⏺ Bash(bun test) │ 引き継ぎを保存しました (コミットなし)。次回起動時に続行を確認します │ ⎿ 3 pass, 1 fail ╰─────────────────────────────────────╯ ● Done. refresh now rejects expired claims and logs an audit event. ✻ Worked for 42s · done 4:20 PM › /handoff ⎿ context-handoff: 引き継ぎを /work/app/.claude/handoff.md に保存しました (コミットなし)。 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── › ? for shortcuts ⚠ context-handoff: ctx 49% (97k/200k) · 5h 31%
README

wanyaldee-skills — Wanyaldee's Skills Package

Wanyaldee の個人スキルパッケージ。Fable 5級のコーディング規律、開発哲学、Fable 5 プロンプティングリファレンス、メモリ規律+監査、認証情報読み取りブロックのフック、Windows→WSL/SSH設定同期フックを収録。

v1.x までは fable-coding という名前だった(旧 URL は GitHub がリダイレクトする)。v2.0.0 で改名。旧名でインストール済みの環境は、/plugin uninstall fable-coding → /plugin marketplace remove fable-coding → 下記の手順で入れ直す。

インストール

Claude Code内で:

/plugin marketplace add Wanyaldee/wanyaldee-skills
/plugin install wanyaldee-skills@wanyaldee-skills

使い方

インストール後、各スキルがタスク内容に応じて自動的に候補になる(コーディングタスク→fable-coding、設計提案→dev-philosophy、メモリ保存→memory-discipline 等)。明示的に使う場合はプロンプトでスキル名を指示する。/memory-audit はコマンドとして直接呼べる。

収録スキル

  • fable-coding — Fable 5級のコーディング規律(計画→根本原因→最小差分→検証→ADR)。v1.4.0で公式プロンプティングガイド由来の行動規則(過剰計画の抑制、ツール結果に基づく進捗報告、assessとfixの境界、最終サマリの可読性、途中終了の禁止)を統合。v1.5.0で @armadillo_ai の7則(完了の機械的定義、複数解釈の確認、ついで改善禁止、証拠付き検証報告、修正2回まで、初見セルフレビュー、確信度+3点報告)を統合。v2.4.0でディレクトリ README の規約(ファイル命名・配置・登録規則)を計画の要件として読む規則を追加(ADR 0011)。v2.5.0で Opus 5 の Claude Code ハーネスが自ら述べている規則(行動の即応性、並列検索、周囲コードへの追従、ミスの率直な承認、結果の忠実報告)を削除して重複を解消(ADR 0012)。v2.7.0でADRフォーマットの記述を writing-adrs スキルへのクロスリファレンスに置き換え(ADR 0014)。v2.8.0で Fable 5.1 ハーネスが自ら述べている規則(結論先出し、file:line 参照、最終サマリの可読性、書式より散文、自力での情報収集、停止条件、assessとfixの境界、状態変更前の証拠確認、意図表明での終了禁止)10項目を削除(ADR 0015)。description をワークフロー要約から発火条件のみに書き換え。
  • writing-adrs (v2.7.0〜) — ADR(Architecture Decision Record)の書き方: Context・Decision・Reason・Alternatives rejected・Consequences・Verification の6節構成。核は Reason 節 —— 実際のコードを掲載した上で、値・アルゴリズム・順序などの個別選択を具体的に説明することを必須とし、「信頼性のため」等のあやふやな一般論を明示的なアンチパターンとして禁止する。AIによってコーディングの負担が軽くなった分、書いた本人がそのコードを説明できなければ責任を取れない、という考え方に基づく(ADR 0014)。
  • prompting-fable-5 (v1.4.0〜) — Fable 5 / Mythos 5 を「使う側」のリファレンス: effort設定、公式スニペット集、サブエージェント/メモリ/send_to_userのスキャフォールディング、refusalフォールバック、旧モデルからのプロンプト移行チェックリスト。Fable 5向けのプロンプト・エージェント・スキルを書くときに発火する。v2.8.0で Fable 5.1 / Mythos 5.1 に対応(ADR 0015): 「Fable 5.1 — what changed」節を追加し、強制ツール使用の400化、履歴のappend-only必須(2026-08-31以降作成のアカウントで thinking ブロックが会話に束縛される)、進捗更新の減少と thinking.display: "updates"、エージェントループでのツール呼び出しバッチ化、散文の高密度化、チャットでの書式不足(旧モデル向けの反書式指示は削除すべき)、引用の無標化、ファイル全体書き換え、低effortでの検索抑制、xhigh/maxでの長文出力、スコープ・テストの過剰、安全分類器の誤検知3パターンを、Anthropicのスニペットを逐語で収録。フォールバック先を Opus 4.8 と Opus 5 の2つに訂正し、max effort と「effortの名前はモデル間で同じ思考量に対応しない」旨を追加。
  • memory-discipline (v1.6.0〜) — 永続オートメモリの規範: origin(モデル+絶対日付)の記録、絶対日付の徹底、索引の同ターン更新、耐久的な事実と揮発的な状態の分離、担当者付きの未確定事項、矛盾に気づいたセッションでの即時修正。2026-07-04 の全プロジェクトメモリ監査の知見を規範化したもの。v2.8.0で Fable 5.1 ハーネスの Memory 節が述べている内容を削除し、差分だけを残す形に縮約(499語→355語、ADR 0015)。
  • memory-audit (v1.7.0〜) — /memory-audit で全プロジェクトのメモリを一発監査: 索引整合、モデル帰属(originSessionId→セッションログ解決)、矛盾・重複・揮発性混在・形式のチェック。デフォルト報告のみ、--fix で機械的修正(索引補完・origin 補記・日付の絶対化)だけ適用。v2.8.0で description を発火条件のみに書き換え(ADR 0015)。
  • injection-vigilance (v2.3.0〜) — プロンプトインジェクション警戒: 指示の出所はユーザー発言とシステム設定のみで、ツールで読んだものはすべてデータ。過去の Claude セッション自身の出力経由の注入(セルフインジェクション)も対象。データ内の指示めいたテキストは実行せず、位置つきで引用報告してユーザーに判断させる。ネスト claude -p での RED/GREEN テスト済み(ADR 0010)。
  • dev-philosophy (v1.3.0〜) — 開発哲学: 自動化の境界線(ヒューマンインザループ、外部信頼サービス優先)、仕組みによるセキュリティ、技術スタック方針(Python/Rust、MariaDB、Proxmox)、MIT License 標準。システム設計・アーキテクチャ提案時に適用される。v2.8.0で description を発火条件のみに書き換え(ADR 0015)。
  • remote-config-sync (v2.6.0〜) — Windowsマシンを正本として ~/.claude/settings.json をWSL/SSH先へ自動プッシュする SessionStart フック。リモート側のハッシュが異なる場合のみ上書きし(旧設定は .bak.<timestamp> に退避)、更新したターゲットのみ報告。同期対象は ~/.claude/remote-sync-targets.conf(リポジトリ非追跡、ユーザー個人管理)で指定。既知の制限として、このマシンを経由せずWSL/SSH側で直接セッションを開始した場合は発火しない。また前提条件として、リモート側にClaude Codeと本プラグインが事前導入されている必要がある(ADR 0013)。

英語スキル(fable-coding, prompting-fable-5, injection-vigilance, memory-discipline, memory-audit, remote-config-sync, writing-adrs)には人間用の和訳 SKILL.ja.md を併設している(v1.4.0〜)。dev-philosophy は本文が日本語のため和訳を持たない。Claude が読み込むのは SKILL.md(英語版)のみ。編集は英語版に行い、和訳を追随させる。

他エージェント向け(AGENTS.md、v2.9.0〜)

SKILL.md は Claude Code 専用の形式(name/description の frontmatter を持ち、Claude が状況に応じて能動的に Skill ツールで呼び出す)であり、/plugin install によるパッケージ配布も Claude Code にしか通用しない。Codex CLI や Cursor など AGENTS.md 規約に従う他エージェントは、この呼び出し機構もインストール機構も持たず、リポジトリ直下の AGENTS.md を常時受動的に読み込むだけである。

そのため、汎用的にどのエージェントにも効く4スキル(fable-coding / dev-philosophy / writing-adrs / injection-vigilance)に限り、AGENTS.md 形式でも提供している。memory-discipline / memory-audit(Claude Codeのオートメモリ機能が前提)と remote-config-sync(Claude CodeのSessionStartフックが前提)はClaude Code固有の仕組みに依存するため対象外(ADR 0017)。

  • リポジトリ直下の AGENTS.md — 上記4スキルを集約した単一ファイル。
  • skills/<name>/AGENTS.md(fable-coding / dev-philosophy / writing-adrs / injection-vigilance の4箇所)— スキル単体の断片。1スキルだけ他プロジェクトへ持ち込みたい場合はこちらをコピーする。

他エージェントでの使い方(/plugin install に相当する導入手順が無いため):

git clone https://github.com/Wanyaldee/wanyaldee-skills

してから、上記いずれかのファイルを対象プロジェクトの AGENTS.md としてコピー(または既存の AGENTS.md に追記)する。fable-coding の §8(スタック方針)や dev-philosophy の技術スタック選定はこのユーザー個人の既定値であり普遍的な標準ではないため、持ち込み先プロジェクトの実情に合わせて書き換えること。

SKILL.md が正本、AGENTS.md は追随して編集する派生物という位置づけは SKILL.ja.md と同じ(編集は SKILL.md に行い、AGENTS.md/SKILL.ja.md をそれぞれ追随させる)。

動作確認の範囲: メンテナーは普段 Claude Code のみを使用しており、Codex CLI や Cursor など他エージェントでの実地テストは行っていない。想定通りに動かない、指示の解釈が食い違う等に気づいた場合は GitHub Issue で報告してほしい。MIT License で配布している OSS なので、直してほしい内容が明確なら Issue を待つより自分で直した方が早いはず — フォークして修正・PR も歓迎する。

Mod: context-handoff(別プラグイン)

Claude Code の function hooks で書いた Mod。スキル本体とは独立した別プラグインとして同じマーケットプレイスに収録している(ソースは mods/context-handoff/)。

/plugin install context-handoff@wanyaldee-skills
  • 常時表示: ステータスラインに ctx 42% (84k/200k) · 5h 63% · 7d 20% を表示。ターン終了ごと、およびレート制限の窓が1ポイント動くたびに更新。レート制限はサブスクリプション利用時のみ表示される。
  • 制限接近時の自動引き継ぎ: レート制限(5h / 7d)のどれか、またはコンテキストが 90% に達したら、セッションごとに1回だけ:
  • セッション自身に作業内容(目的 / 完了 / 残り / 次の一手 / 関連ファイル)を要約させる(失敗時は直近3件の依頼原文)
  • <repo>/.claude/handoff.md に書き出す
  • git add -A → chore: handoff checkpoint before token limit でコミット
  • リポジトリのパスをキーに記録(SessionID は使わない)
  • 実行中のエージェントに「新しい作業を始めず区切りで止めて報告」と伝える
  • 次回起動時の確認: 同じリポジトリで起動すると、プロンプト上に「このリポジトリ (…) でこの作業をやっていましたがトークン切れで作業を一時中断しました、続行しますか」と出る。[続行する](y)で引き継ぎメモ付きの続きの依頼を送信、[破棄する](n)で記録を削除。答えるまで消えない。
  • /handoff で手動の引き継ぎも可能。

注意: チェックポイントは現在のブランチに、.gitignore 対象外の未追跡ファイルも含めてコミットする。判定はターン終了時のみなので、1ターンの途中で 100% に達した場合は間に合わない。詳細は ADR 0018。

認証情報ファイルの読み取りブロック(v1.2.0〜)

プラグインを有効化すると PreToolUse フックが自動で登録され、Claude が以下のファイルを Read ツールで読むことを拒否する:

  • .env / .env.*(ただし .env*.example / .env*.sample / .env*.template は必要なキー名の把握のため読み取り可、v2.1.0〜)
  • *.pem / id_rsa / id_ed25519
  • credentials.json / .aws/ 配下

加えて Bash 用フック(v2.2.0〜)が以下のコマンドを拒否する:

  • .env 系ファイルへの言及(テンプレートは除外)— cat .env など
  • mkfs / dd of=/dev/*
  • rm -rf のうち、対象を事前に提示できないもの — グロブ(build/*)、変数・コマンド置換($DIR、` pwd )、. / ..、/ と最上位パス、ホームディレクトリ自身、/etc /usr /root などシステム配下、/var /tmp /home 等の完全一致、xargs/find -exec` 経由、引用符が壊れて解析できないもの
  • DB クライアント直叩き — mysql / mariadb / psql / sqlite3 / mongosh / redis-cli / wrangler d1 execute|migrations apply。実行するステートメントはユーザーに報告し、ユーザー自身が実行する(fable-coding セクション4の仕組み化)。

v2.8.0〜、rm -rf は一律拒否ではなくなった(ADR 0016)。対象がすべて保護対象外のリテラルなパスであれば、拒否ではなく確認プロンプト(permissionDecision: "ask")を出し、削除対象のパスを示した上でユーザーが承認・却下する。完了済み worktree や node_modules の掃除がこれで通る。上の一覧にある「対象を事前に提示できないもの」は従来どおり拒否のままで、1つのコマンド行に両方が含まれる場合は拒否が優先する。あわせて、パス付きバイナリ(/bin/rm -rf /)が v2.2.0〜v2.7.0 のフックをすり抜けていた穴を修正した。

制限: 事故防止のガードであり敵対的境界ではない(詳細は ADR 0001 / 0009 / 0016)。念のため settings.json の permissions.deny に Read(**/.env) 等を併設しておくとフック無効時の保険になる。

License

MIT

Source 3 files
hooks/register.tsx 173 lines
1import { atom, read, update } from 'claude-code'
2import type { EngineInterface, Register, SessionContextUsage, SessionRateLimit } from 'claude-code'
3
4import type { Handoff } from '../types'
5import {
6  HANDOFF_FILE,
7  SUMMARY_PROMPT,
8  commitMessage,
9  formatStatus,
10  handoffMarkdown,
11  limitReason,
12  resumePrompt,
13  resumeQuestion,
14  storeKey,
15  wrapUpNote,
16} from './logic'
17
18const pending = atom({ plugin: 'context-handoff', key: 'pending' } as const, null)
19const hasSavedThisSession = atom({ plugin: 'context-handoff', key: 'hasSavedThisSession' } as const, false)
20
21const FALLBACK_PROMPTS = 3
22
23async function git($: EngineInterface, args: string[], cwd: string): Promise<{ isOk: boolean; out: string }> {
24  try {
25    const ran = await $.process.run(['git', ...args], { cwd })
26    return { isOk: ran.exitCode === 0, out: ran.stdout.trim() }
27  } catch (error) {
28    $.ui.log(`context-handoff: git ${args[0]} failed: ${String(error)}`)
29    return { isOk: false, out: '' }
30  }
31}
32
33async function repoRootOf($: EngineInterface): Promise<string> {
34  const cwd = await $.session.cwd()
35  const root = await git($, ['rev-parse', '--show-toplevel'], cwd)
36
37  return root.isOk && root.out !== '' ? root.out : cwd
38}
39
40/** Asks the session itself what it was doing; falls back to the last prompts typed. */
41async function summarize($: EngineInterface): Promise<string> {
42  const reply = await $.model.fork({ prompt: SUMMARY_PROMPT })
43  if (reply.isAnswered && reply.text.trim() !== '') {
44    return reply.text.trim()
45  }
46  $.ui.log(`context-handoff: summary fork gave no text (${reply.isAnswered ? 'empty' : reply.reason}), using recent prompts`)
47  const messages = await $.session.messages()
48  const prompts = messages
49    .filter(message => message.role === 'user' && message.text.trim() !== '')
50    .slice(-FALLBACK_PROMPTS)
51    .map(message => `- ${message.text.trim().slice(0, 500)}`)
52
53  return ['## 直近の依頼 (自動要約に失敗したため原文)', ...prompts].join('\n')
54}
55
56/** Stages everything .gitignore allows and commits it; answers the short sha, or '' when nothing was committed. */
57async function commitAll($: EngineInterface, repoRoot: string, reason: string): Promise<string> {
58  const isRepo = (await git($, ['rev-parse', '--is-inside-work-tree'], repoRoot)).isOk
59  if (!isRepo) {
60    return ''
61  }
62  await git($, ['add', '-A'], repoRoot)
63  const hasStaged = !(await git($, ['diff', '--cached', '--quiet'], repoRoot)).isOk
64  if (!hasStaged) {
65    return ''
66  }
67  const committed = await git($, ['commit', '-m', commitMessage(reason)], repoRoot)
68  if (!committed.isOk) {
69    $.ui.toast('context-handoff: チェックポイントのコミットに失敗しました (pre-commit hook 等)。変更はステージ済みです')
70    return ''
71  }
72
73  return (await git($, ['rev-parse', '--short', 'HEAD'], repoRoot)).out
74}
75
76async function checkpoint($: EngineInterface, reason: string): Promise<Handoff> {
77  await update($, hasSavedThisSession, () => true)
78  $.ui.toast(`${reason}。作業を記録してコミットします…`)
79
80  const repoRoot = await repoRootOf($)
81  const branch = (await git($, ['rev-parse', '--abbrev-ref', 'HEAD'], repoRoot)).out || '(なし)'
82  const createdAt = new Date(await $.clock.now()).toISOString()
83  const summary = await summarize($)
84  const draft: Handoff = { repoRoot, branch, commit: '', reason, summary, createdAt }
85
86  await $.fs.write(`${repoRoot}/${HANDOFF_FILE}`, handoffMarkdown(draft))
87  const handoff: Handoff = { ...draft, commit: await commitAll($, repoRoot, reason) }
88  await $.store.set(storeKey(repoRoot), handoff)
89
90  await $.session.append({ message: { type: 'user', content: [{ type: 'text', text: wrapUpNote(handoff) }] } })
91  $.ui.toast(`引き継ぎを保存しました (${handoff.commit || 'コミットなし'})。次回起動時に続行を確認します`)
92
93  return handoff
94}
95
96function showUsage($: EngineInterface, context: SessionContextUsage, rateLimits: readonly SessionRateLimit[]) {
97  $.ui.status(formatStatus(context, rateLimits))
98}
99
100async function settle($: EngineInterface, handoff: Handoff) {
101  await $.store.delete(storeKey(handoff.repoRoot))
102  await update($, pending, () => null)
103}
104
105export const register: Register = on => {
106  on('session.start', async ($, e, next) => {
107    await $.command.register({
108      name: 'handoff',
109      description: '今の作業を記録・コミットし、次のセッションへ引き継ぐ',
110    })
111    const usage = await $.session.usage()
112    showUsage($, usage.context, usage.rateLimits)
113
114    // A hot reload fires session.start again: never offer this session's own handoff back to it.
115    const isOwnHandoff = await read($, hasSavedThisSession)
116    if (e.isInteractive && !isOwnHandoff) {
117      const saved = (await $.store.get(storeKey(await repoRootOf($)))) as Handoff | undefined
118      if (saved !== undefined) {
119        await update($, pending, () => saved)
120        $.ui.toast(resumeQuestion(saved))
121      }
122    }
123
124    return next(e)
125  })
126
127  on('session.measure', async ($, e, next) => {
128    showUsage($, e.context, e.rateLimits)
129    const reason = limitReason(e.context, e.rateLimits)
130    if (reason !== null && !(await read($, hasSavedThisSession))) {
131      await checkpoint($, reason)
132    }
133
134    return next(e)
135  })
136
137  on('command.run', { command: 'handoff' }, async $ => {
138    const handoff = await checkpoint($, '手動で引き継ぎを要求')
139
140    return { text: `引き継ぎを ${handoff.repoRoot}/${HANDOFF_FILE} に保存しました (${handoff.commit || 'コミットなし'})。` }
141  })
142
143  on('ui.render', { component: 'AbovePrompt' }, async ($, e, next) => {
144    const handoff = await read($, pending)
145    if (handoff === null) {
146      return next(e)
147    }
148    const { Box, Button, Text } = $.ui.resolve(e)
149
150    return (
151      <Box flexDirection="column" borderStyle="round" paddingX={1}>
152        <Text bold>{resumeQuestion(handoff)}</Text>
153        <Text dimColor>
154          {handoff.reason} · {handoff.branch} · {handoff.commit || 'コミットなし'} · {handoff.createdAt}
155        </Text>
156        <Box>
157          <Button
158            key="resume"
159            label="続行する"
160            hotkey="y"
161            variant="primary"
162            onPress={async () => {
163              await settle($, handoff)
164              await $.prompt.submit({ text: resumePrompt(handoff), asUser: true })
165            }}
166          />
167          <Button key="discard" label="破棄する" hotkey="n" onPress={() => settle($, handoff)} />
168        </Box>
169      </Box>
170    )
171  })
172}
173
hooks/logic.ts 91 lines
1import type { SessionContextUsage, SessionRateLimit } from 'claude-code'
2
3import type { Handoff } from '../types'
4
5export const CONTEXT_THRESHOLD = 90
6export const RATE_LIMIT_THRESHOLD = 90
7export const HANDOFF_FILE = '.claude/handoff.md'
8
9const LIMIT_LABELS: Record<string, string> = {
10  five_hour: '5h',
11  seven_day: '7d',
12  spend_limit: 'spend',
13}
14
15export const SUMMARY_PROMPT = [
16  'トークン制限が近いため、この作業を次のエージェントに引き継ぎます。',
17  '次のエージェントが会話履歴なしで作業を再開できるよう、日本語のMarkdownで以下を簡潔にまとめてください。',
18  '見出しは「## 目的」「## 完了したこと」「## 残っていること」「## 次の一手」「## 関連ファイル」の5つ。',
19  '推測は推測と明記し、まとめ以外の前置きは書かないでください。',
20].join('\n')
21
22const formatTokens = (n: number): string => (n >= 1000 ? `${Math.round(n / 1000)}k` : String(n))
23
24export const limitLabel = (kind: string): string => LIMIT_LABELS[kind] ?? kind
25
26export function formatStatus(context: SessionContextUsage, rateLimits: readonly SessionRateLimit[]): string {
27  const ctx =
28    context.percent === undefined || context.tokens === undefined
29      ? `ctx --/${formatTokens(context.window)}`
30      : `ctx ${context.percent}% (${formatTokens(context.tokens)}/${formatTokens(context.window)})`
31  const limits = rateLimits.map(limit => `${limitLabel(limit.kind)} ${limit.percentUsed}%`)
32
33  return [ctx, ...limits].join(' · ')
34}
35
36/** Why a handoff is due, or null while every figure is under its threshold. */
37export function limitReason(context: SessionContextUsage, rateLimits: readonly SessionRateLimit[]): string | null {
38  const hot = rateLimits.find(limit => limit.percentUsed >= RATE_LIMIT_THRESHOLD)
39  if (hot !== undefined) {
40    return `レート制限 ${limitLabel(hot.kind)} が ${hot.percentUsed}% に到達`
41  }
42  if (context.percent !== undefined && context.percent >= CONTEXT_THRESHOLD) {
43    return `コンテキストが ${context.percent}% に到達`
44  }
45
46  return null
47}
48
49export const storeKey = (repoRoot: string): string => `handoff:${repoRoot}`
50
51export const commitMessage = (reason: string): string =>
52  `chore: handoff checkpoint before token limit\n\n${reason}. Work in progress, see ${HANDOFF_FILE}.`
53
54export function handoffMarkdown(handoff: Handoff): string {
55  return [
56    '# 作業引き継ぎ (context-handoff)',
57    '',
58    `- リポジトリ: ${handoff.repoRoot}`,
59    `- ブランチ: ${handoff.branch}`,
60    `- 理由: ${handoff.reason}`,
61    `- 記録日時: ${handoff.createdAt}`,
62    '',
63    handoff.summary,
64    '',
65  ].join('\n')
66}
67
68export const resumeQuestion = (handoff: Handoff): string =>
69  `このリポジトリ (${handoff.repoRoot}) でこの作業をやっていましたがトークン切れで作業を一時中断しました、続行しますか`
70
71export function resumePrompt(handoff: Handoff): string {
72  const commitLine =
73    handoff.commit === '' ? 'チェックポイントのコミットはありません。' : `チェックポイントのコミット: ${handoff.commit}`
74
75  return [
76    '前のセッションがトークン制限で中断した作業の続きをお願いします。',
77    `ブランチ ${handoff.branch}。${commitLine}`,
78    `引き継ぎメモ (${HANDOFF_FILE} と同じ内容):`,
79    '',
80    handoff.summary,
81    '',
82    `まず git log と git status で状態を確認してから「次の一手」から再開してください。作業が終わったら ${HANDOFF_FILE} を削除してください。`,
83  ].join('\n')
84}
85
86export const wrapUpNote = (handoff: Handoff): string =>
87  [
88    `[context-handoff] ${handoff.reason}。作業内容を ${HANDOFF_FILE} に記録し、チェックポイントをコミットしました (${handoff.commit || 'コミットなし'})。`,
89    '新しい大きな作業は始めず、今のステップを区切りのよいところで終え、ユーザーに中断を報告してください。',
90  ].join('\n')
91
types/index.d.ts 18 lines
1export type Handoff = {
2  repoRoot: string
3  branch: string
4  commit: string
5  reason: string
6  summary: string
7  createdAt: string
8}
9
10declare module 'claude-code' {
11  interface PluginState {
12    'context-handoff': {
13      pending: Handoff | null
14      hasSavedThisSession: boolean
15    }
16  }
17}
18