| name | session-from-r2 | ||
|---|---|---|---|
| description | 過去のセッションログ (R2) を読み込んで、セッションコンテキストを復元する。 Cloudflare R2 に保存された JSONL 形式のセッションログを取得・要約する。 トリガー: /session-from-r2、「前回の引き継ぎ読んで」「ハンドオーバー確認して」 「前回どこまでやった?」「前のセッションの続きから」 引数でセッション ID を直接指定可能。省略時は R2 から自動検索。 | ||
| allowed-tools |
|
過去のセッションログ (R2) を読み込んで、セッションコンテキストを復元する。
R2 アクセスには sops-secrets 経由の S3 互換クレデンシャルが必要。 session-to-r2 と同じ認証方式 (curl + S3v4 署名) を使用する。 wrangler は不要。
| 変数 | 必須 | 説明 |
|---|---|---|
SESSION_R2_BUCKET |
Yes | R2 バケット名 (default: claude-sessions) |
R2_S3_ACCESS_KEY_ID |
Yes | R2 S3 Access Key ID |
R2_S3_SECRET_ACCESS_KEY |
Yes | R2 S3 Secret Access Key |
R2_S3_ENDPOINT |
Yes | R2 S3 endpoint URL |
sops-secrets でロード済みであれば自動で利用可能。
Minimize commands. Each R2 API call has latency — never fetch the same object twice.
# sops-secrets から R2 クレデンシャルをロード (未設定の場合)
if [ -z "${R2_S3_ACCESS_KEY_ID:-}" ]; then
SOPS_FILE=""
for _candidate in \
"$(git rev-parse --show-toplevel 2>/dev/null)/secrets/agents.enc.env" \
"$HOME/.agents/skills/secrets/agents.enc.env" \
; do
[ -f "$_candidate" ] && SOPS_FILE="$_candidate" && break
done
unset _candidate
if [ -n "$SOPS_FILE" ] && command -v sops &>/dev/null; then
export SOPS_AGE_KEY_FILE="${SOPS_AGE_KEY_FILE:-$HOME/.config/sops/age/keys.txt}"
while IFS='=' read -r k v; do [ -n "$k" ] && export "$k=$v"; done \
< <(sops decrypt --input-type dotenv --output-type dotenv "$SOPS_FILE" 2>/dev/null)
fi
fi
export SESSION_R2_BUCKET="${SESSION_R2_BUCKET:-claude-sessions}"If a session ID is given as argument, skip to Step 3.
Otherwise, discover available sessions:
REPO_PREFIX=$(git remote get-url origin 2>/dev/null | sed -E 's#.*[:/]([^/]+)/([^/.]+)(\.git)?$#\1/\2#')
# R2 S3 API で session 一覧を取得
~/.agents/scripts/r2-s3v4.sh list "$SESSION_R2_BUCKET" "$REPO_PREFIX/" | \
python3 -c "
import sys, json
from collections import defaultdict
data = json.loads(sys.stdin.read())
sessions = defaultdict(list)
for obj in data.get('Contents', []):
key = obj['Key']
parts = key.split('/')
if len(parts) >= 3:
session_id = parts[2]
sessions[session_id].append(obj)
for sid, objs in sorted(sessions.items(),
key=lambda x: max(o['Key'] for o in x[1]), reverse=True)[:10]:
latest = max(o['Key'] for o in objs)
total_size = sum(int(o.get('Size', 0)) for o in objs)
print(f'{sid} chunks={len(objs)} size={total_size//1024}KB latest={latest.split(\"/\")[-1]}')
"SESSION_ID="<selected-session-id>"
# 最新チャンクのキーを取得
LATEST_KEY=$(~/.agents/scripts/r2-s3v4.sh list "$SESSION_R2_BUCKET" "$REPO_PREFIX/$SESSION_ID/" | \
python3 -c "
import sys, json
data = json.loads(sys.stdin.read())
keys = sorted([o['Key'] for o in data.get('Contents', [])])
if keys: print(keys[-1])
")
# ダウンロード
~/.agents/scripts/r2-s3v4.sh get "$SESSION_R2_BUCKET" "$LATEST_KEY" /tmp/session-chunk.jsonl
# パース・要約
python3 -c '
import sys, json
first_ts = last_ts = None
user_msgs, asst_msgs = [], []
for line in open("/tmp/session-chunk.jsonl"):
try: obj = json.loads(line)
except: continue
ts = obj.get("timestamp", "")
if not first_ts and ts: first_ts = ts
if ts: last_ts = ts
role, msg = obj.get("type", ""), obj.get("message", {})
if not isinstance(msg, dict): continue
content = msg.get("content", "")
if isinstance(content, list):
content = " ".join(c.get("text", "") for c in content if isinstance(c, dict) and c.get("type") == "text")
if not content: continue
if role == "user" and len(content) < 500:
user_msgs.append((ts[11:19] if len(ts) > 19 else ts, content[:200]))
elif role == "assistant" and len(content) > 20:
asst_msgs.append((ts[11:19] if len(ts) > 19 else ts, content[:300]))
print(f"Range: {first_ts} -> {last_ts}")
print(f"Events: {len(user_msgs)} user, {len(asst_msgs)} assistant")
print("--- User messages ---")
for ts, m in user_msgs: print(f"[{ts}] {m}")
print("--- Key assistant messages (last 15) ---")
for ts, m in asst_msgs[-15:]: print(f"[{ts}] {m}")
'From the latest chunk output, report:
- Chunk info and total chunk count
- What was done, key decisions, issues, outcomes
- Any incomplete tasks or next steps
Then ask: if the user needs more context from earlier in the session, fetch additional chunks (work backwards from the second-to-last chunk). Do not pre-fetch all chunks.
- Never fetch the same R2 object more than once.
- Always read the latest chunk by default.
- Keep the summary concise — the user can ask for details.
- Focus on assistant text messages (user messages often contain verbose tool results).
- Report the session date and time range.
- If the session references files, verify they still exist before suggesting actions.
- Clean up temp files:
rm -f /tmp/session-chunk.jsonlafter reading.