フック(自動想起)
agent-team-pack の「自動想起」は、Claude Code のフック機構を使って実現されています。 ここが「LLM を呼ばずに前回の文脈を復元する」というこのパッケージの目玉トリックです。
仕掛けの核心:SessionStart の stdout 注入
Claude Code には「SessionStart フックの 標準出力(stdout)が、そのままセッションコンテキストに注入される」という仕様があります。agent-team-pack はこれを利用して、
ローカルツールが組み立てたテキストを stdout に書く → それがエージェントの文脈に入る
という形で、LLM を一度も呼ばずに「前回までの作業文脈」と「記憶の目次」を自動投入します。memory_start.sh のコメントにもこう書かれています。
Claude Code の仕様: SessionStart フックの stdout はセッションコンテキストに注入される。LLM を呼ばずに「前回までの文脈」を自動で渡す(hermes の自動想起の再現)。
フックの登録:hooks/hooks.json
プラグインは 2 つのフックを登録します。
{
"hooks": {
"SessionStart": [
{ "hooks": [
{ "type": "command",
"command": "bash \"${CLAUDE_PLUGIN_ROOT}/hooks/memory_start.sh\"",
"timeout": 20 }
] }
],
"Stop": [
{ "hooks": [
{ "type": "command",
"command": "bash \"${CLAUDE_PLUGIN_ROOT}/hooks/memory_stop.sh\"",
"timeout": 30, "async": true }
] }
]
}
}- SessionStart:
memory_start.sh(timeout 20 秒、同期)。注入はセッション開始のブロッキング処理として実行。 - Stop:
memory_stop.sh(timeout 30 秒、async: true)。終了処理はセッション終了をブロックしない。
${CLAUDE_PLUGIN_ROOT} は Claude Code がプラグイン導入先に展開する変数です。
SessionStart:memory_start.sh の処理
# 1) userConfig(GUI 設定)→ 環境変数の配線。明示的な MEMORY_DIRS が優先。
if [ -z "${MEMORY_DIRS:-}" ] && [ -n "${CLAUDE_PLUGIN_OPTION_memory_dirs:-}" ]; then
export MEMORY_DIRS="${CLAUDE_PLUGIN_OPTION_memory_dirs}"
fi
# 2) 索引DBは plugin 更新で消えない場所(CLAUDE_PLUGIN_DATA)に置く
export MEMORY_INDEX_DB="${MEMORY_INDEX_DB:-${CLAUDE_PLUGIN_DATA:-$ROOT/memory_system}/memory_index.db}"
# 3) stdin の JSON から source(startup/resume/clear/compact)を読む
SOURCE="$(python3 -c 'import json,sys; print(json.load(sys.stdin).get("source",""))' 2>/dev/null || true)"
# 4) 記憶を差分再索引(出力は捨てる)
python3 "$ROOT/memory_system/index_memory.py" reindex >/dev/null 2>&1 || true
# 5) 注入テキストを stdout に出す(これがコンテキストに入る)
if [ "${MEMORY_INJECT:-1}" != "0" ]; then
BRIEF=""
[ "$SOURCE" = "compact" ] && BRIEF="--brief" # compact 直後は短縮
python3 "$ROOT/memory_system/session_context.py" $BRIEF 2>/dev/null || true
fi
exit 0注目すべき実装上の判断がいくつかあります。
source による分岐(compact 対応)
フックは stdin で渡される JSON の source(startup / resume / clear / compact)を読みます。compact 直後はコンテキスト要約に直近の文脈がまだ残っているため、--brief(ヘッダのみの短縮注入)に切り替え、二重注入を避けます。
索引 DB を「消えない場所」に置く
MEMORY_INDEX_DB の既定は CLAUDE_PLUGIN_DATA 配下です。プラグインを更新してもデータが消えない領域に索引を置き、なおかつ「md から再構築可能な影」なので消えても問題ない、という二段構えです。
何があっても exit 0
各コマンドは || true / 2>/dev/null で失敗を握りつぶし、最後は必ず exit 0 します。記憶システムの不調がセッション開始を妨げないことを最優先にした設計です。
注入されるテキストの中身:session_context.py
実際にコンテキストへ入る文字列を組み立てるのが session_context.py です。「目次方式」 を採用しているのがポイントで、記憶の本文は注入しません。
注入は 3 部構成です。
- 固定ヘッダ — 記憶システムの使い方の常設指示。
- CONTEXT.md の先頭 N ブロック — 直近の作業文脈(既定 1 ブロック)。
- 記憶 md の目次 — ディレクトリごとのファイル名一覧(「何の記憶があるか」だけ)。
固定ヘッダは次の内容で、エージェントに recall/remember の使用を常に促します。
[永続記憶システム]
- 過去の決定・文脈が必要なとき・作業を始める前は recall スキルで記憶を検索する。
- 設計決定・ユーザーの好み・恒久的な事実が会話に出たら、その場で remember スキルで保存する(後回しにしない)。
- 日付は必ず絶対表記(YYYY-MM-DD)。「昨日」「来週」は禁止。なぜ目次方式なのか
本文を全部注入すると、記憶が増えるほど注入量が膨らみ、毎セッションのトークンを圧迫します。そこで
- 注入は「ヘッダ+最新ブロック+ファイル名一覧」だけに留め、
- 上限
MEMORY_INJECT_MAX_CHARS(既定 4000 字)でクランプし、 - 詳細が必要なときだけ recall(FTS5 検索)で引く
という設計にしています。これにより 記憶の総量が増えても注入量はほぼ一定に保たれます。build_context() は上限超過時に末尾を切り、「続きは recall で検索」と案内します。
text = "\n".join(parts).strip()
if len(text) > max_chars:
text = text[:max_chars].rstrip() + "\n…(注入上限により省略。続きは recall で検索)"注入失敗時は except Exception: pass で何も出力せず終了し、ここでもセッション開始を止めません。
Stop:memory_stop.sh の処理
セッション終了時には CONTEXT.md のローテと記憶の再索引を行います。
# MEMORY_CONTEXT が指定され、ファイルがあればローテーション
if [ -n "${MEMORY_CONTEXT:-}" ] && [ -f "${MEMORY_CONTEXT}" ]; then
python3 "$ROOT/memory_system/rotate_context.py" "${MEMORY_CONTEXT}" -n "${MEMORY_KEEP_N:-5}" \
>/dev/null 2>&1 || true
fi
python3 "$ROOT/memory_system/index_memory.py" reindex >/dev/null 2>&1 || true
exit 0async: true なので終了処理がセッションを待たせません。ただし前述の通り Stop はクラッシュ時に発火しないため、索引の鮮度は最終的に「検索時の差分 reindex」でも担保される二重化になっています(記憶層参照)。
設定(環境変数)
すべて任意で、未設定でも動きます。
| 変数 | 既定 | 意味 |
|---|---|---|
MEMORY_DIRS | userConfig → ~/.claude | 記憶 md のディレクトリ(os.pathsep 区切り) |
MEMORY_CONTEXT | 記憶ディレクトリ直下を探索 | CONTEXT.md のパス |
MEMORY_KEEP_N | 5 | ローテで残す先頭ブロック数 |
MEMORY_INDEX_DB | CLAUDE_PLUGIN_DATA 配下 | 索引 DB の場所 |
MEMORY_INJECT | 1 | 0 で自動注入を停止(再索引のみ) |
MEMORY_INJECT_MAX_CHARS | 4000 | 自動注入の上限字数 |
MEMORY_INJECT_BLOCKS | 1 | CONTEXT.md から注入する先頭ブロック数 |
記憶ディレクトリの指定は、環境変数 MEMORY_DIRS のほか、プラグイン設定 GUI(/plugin configure agent-team-pack)の memory_dirs でも可能で、フックがその値を環境変数へ配線します(明示的な MEMORY_DIRS が優先)。