toolkit/packages/skills/recover-sessions/SKILL.md
Recover crashed or orphaned agent sessions
npx skillsauth add stevengonsalvez/agents-in-a-box recover-sessionsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Recover agent sessions after tmux crash, system shutdown, or disconnect. Lists orphaned sessions (tmux dead, worktree exists), allows resuming with transcript, or cleanup.
/recover-sessions # List orphaned sessions
/recover-sessions list # List orphaned sessions (same as above)
/recover-sessions status # Show summary statistics
/recover-sessions resume <session> # Resume session in new tmux
/recover-sessions cleanup <session> # Archive session and optionally remove worktree
/recover-sessions cleanup-all # Archive all orphaned sessions
#!/bin/bash
set -euo pipefail
# Source the spawn-agent library
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/../utils/spawn-agent-lib.sh"
AGENTS_DIR="${HOME}/.claude/agents"
ARCHIVED_DIR="${AGENTS_DIR}/archived"
# Ensure directories exist
ensure_agent_dirs
# Parse command
CMD="${1:-list}"
shift 2>/dev/null || true
case "$CMD" in
list|"")
echo "==============================================================="
echo " Orphaned Agent Sessions"
echo "==============================================================="
echo ""
ORPHAN_COUNT=0
RESUMABLE_COUNT=0
for META_FILE in "$AGENTS_DIR"/*.json; do
[ -f "$META_FILE" ] || continue
[ "$(basename "$META_FILE")" = "registry.jsonl" ] && continue
SESSION=$(jq -r '.session // empty' "$META_FILE" 2>/dev/null)
[ -z "$SESSION" ] && continue
STATUS=$(jq -r '.status // "unknown"' "$META_FILE" 2>/dev/null)
[ "$STATUS" = "completed" ] && continue
[ "$STATUS" = "archived" ] && continue
# Check if tmux session exists
if tmux has-session -t "$SESSION" 2>/dev/null; then
continue # Still alive, not orphaned
fi
WORKTREE=$(jq -r '.directory // empty' "$META_FILE" 2>/dev/null)
[ -z "$WORKTREE" ] && continue
[ ! -d "$WORKTREE" ] && continue
((ORPHAN_COUNT++)) || true
TASK=$(jq -r '.task // "unknown"' "$META_FILE" | head -c 60 | tr '\n' ' ')
CREATED=$(jq -r '.created // "unknown"' "$META_FILE")
BRANCH=$(jq -r '.worktree_branch // ""' "$META_FILE")
# Check for transcript
TRANSCRIPT=$(jq -r '.transcript_path // empty' "$META_FILE")
if [ -z "$TRANSCRIPT" ] || [ ! -f "$TRANSCRIPT" ]; then
TRANSCRIPT=$(find_transcript_path "$WORKTREE")
fi
CAN_RESUME="No"
if [ -n "$TRANSCRIPT" ] && [ -f "$TRANSCRIPT" ]; then
CAN_RESUME="Yes"
((RESUMABLE_COUNT++)) || true
fi
echo " Session: $SESSION"
echo " Task: ${TASK}..."
echo " Dir: $WORKTREE"
[ -n "$BRANCH" ] && echo " Branch: $BRANCH"
echo " Created: $CREATED"
echo " Resume: $CAN_RESUME"
echo ""
done
if [ "$ORPHAN_COUNT" -eq 0 ]; then
echo " No orphaned sessions found."
echo ""
else
echo "---------------------------------------------------------------"
echo " Total orphaned: $ORPHAN_COUNT"
echo " Resumable: $RESUMABLE_COUNT"
echo ""
echo " Commands:"
echo " /recover-sessions resume <session> - Resume in new tmux"
echo " /recover-sessions cleanup <session> - Archive session"
fi
echo "==============================================================="
;;
status)
echo "==============================================================="
echo " Session Recovery Status"
echo "==============================================================="
echo ""
# Count active sessions
ACTIVE_COUNT=0
ORPHAN_COUNT=0
ARCHIVED_COUNT=0
for META_FILE in "$AGENTS_DIR"/*.json; do
[ -f "$META_FILE" ] || continue
[ "$(basename "$META_FILE")" = "registry.jsonl" ] && continue
SESSION=$(jq -r '.session // empty' "$META_FILE" 2>/dev/null)
[ -z "$SESSION" ] && continue
STATUS=$(jq -r '.status // "unknown"' "$META_FILE")
if tmux has-session -t "$SESSION" 2>/dev/null; then
((ACTIVE_COUNT++)) || true
elif [ "$STATUS" != "completed" ] && [ "$STATUS" != "archived" ]; then
((ORPHAN_COUNT++)) || true
fi
done
# Count archived
if [ -d "$ARCHIVED_DIR" ]; then
ARCHIVED_COUNT=$(find "$ARCHIVED_DIR" -maxdepth 1 -name "*.json" -type f 2>/dev/null | wc -l | tr -d ' ')
fi
echo " Active (tmux live): $ACTIVE_COUNT"
echo " Orphaned: $ORPHAN_COUNT"
echo " Archived: $ARCHIVED_COUNT"
echo ""
echo "==============================================================="
;;
resume)
SESSION="${1:-}"
if [ -z "$SESSION" ]; then
echo "Session name required"
echo "Usage: /recover-sessions resume <session>"
echo ""
echo "Run '/recover-sessions list' to see orphaned sessions"
exit 1
fi
# Allow partial match
if [ ! -f "${AGENTS_DIR}/${SESSION}.json" ]; then
# Try to find matching session
MATCH=$(find "$AGENTS_DIR" -maxdepth 1 -name "*${SESSION}*.json" -type f 2>/dev/null | head -1)
if [ -n "$MATCH" ]; then
SESSION=$(basename "$MATCH" .json)
fi
fi
resume_session "$SESSION"
;;
cleanup)
SESSION="${1:-}"
if [ -z "$SESSION" ]; then
echo "Session name required"
echo "Usage: /recover-sessions cleanup <session>"
exit 1
fi
META_FILE="${AGENTS_DIR}/${SESSION}.json"
if [ ! -f "$META_FILE" ]; then
# Try partial match
MATCH=$(find "$AGENTS_DIR" -maxdepth 1 -name "*${SESSION}*.json" -type f 2>/dev/null | head -1)
if [ -n "$MATCH" ]; then
META_FILE="$MATCH"
SESSION=$(basename "$MATCH" .json)
fi
fi
if [ ! -f "$META_FILE" ]; then
echo "Session not found: $SESSION"
exit 1
fi
WORKTREE=$(jq -r '.directory // empty' "$META_FILE")
WITH_WORKTREE=$(jq -r '.with_worktree // false' "$META_FILE")
BRANCH=$(jq -r '.worktree_branch // empty' "$META_FILE")
echo "Archiving session: $SESSION"
archive_session "$SESSION"
echo "Session archived to: ${ARCHIVED_DIR}/${SESSION}.json"
# Offer worktree cleanup if applicable
if [ "$WITH_WORKTREE" = "true" ] && [ -n "$WORKTREE" ] && [ -d "$WORKTREE" ]; then
echo ""
echo "Worktree still exists at: $WORKTREE"
echo " Branch: $BRANCH"
echo ""
echo " To remove worktree and branch:"
echo " git worktree remove \"$WORKTREE\" && git branch -D \"$BRANCH\""
echo ""
echo " Or use: /cleanup-agent-worktree ${SESSION#agent-}"
fi
;;
cleanup-all)
echo "Archiving all orphaned sessions..."
echo ""
ARCHIVED=0
for META_FILE in "$AGENTS_DIR"/*.json; do
[ -f "$META_FILE" ] || continue
[ "$(basename "$META_FILE")" = "registry.jsonl" ] && continue
SESSION=$(jq -r '.session // empty' "$META_FILE" 2>/dev/null)
[ -z "$SESSION" ] && continue
STATUS=$(jq -r '.status // "unknown"' "$META_FILE")
[ "$STATUS" = "completed" ] && continue
[ "$STATUS" = "archived" ] && continue
# Check if tmux session exists
if ! tmux has-session -t "$SESSION" 2>/dev/null; then
echo " Archiving: $SESSION"
archive_session "$SESSION"
((ARCHIVED++)) || true
fi
done
echo ""
echo "Archived $ARCHIVED sessions"
echo ""
echo "Note: Worktrees were NOT removed. Use /list-agent-worktrees to see them."
;;
help|*)
echo "==============================================================="
echo " /recover-sessions - Recover Orphaned Agent Sessions"
echo "==============================================================="
echo ""
echo " Commands:"
echo ""
echo " list List all orphaned sessions"
echo " status Show summary: active, orphaned, archived"
echo " resume <session> Resume session in new tmux with transcript"
echo " cleanup <session> Archive session metadata"
echo " cleanup-all Archive all orphaned sessions"
echo " help Show this help"
echo ""
echo " What is an orphaned session?"
echo " A session where tmux died but the worktree/metadata still exists."
echo " This happens after system shutdown, network disconnect, or crash."
echo ""
echo " Resuming sessions:"
echo " If a transcript exists, the session can be resumed with full"
echo " conversation history using 'claude --resume'."
echo ""
echo "==============================================================="
;;
esac
exit 0
{{HOME_TOOL_DIR}}/projects/){{HOME_TOOL_DIR}}/agents/archived/ - worktrees are NOT auto-removed/cleanup-agent-worktree or manual git commands to remove worktrees after archiving/recover-sessions list/recover-sessions resume <session>/recover-sessions cleanup <session> then /cleanup-agent-worktree/recover-sessions cleanup-all to archive all orphaned metadatadocumentation
Report reflect drain spend over a time window — tokens split by cached (cache_read), uncached writes (cache_creation), and io (input+output), with a $ estimate, grouped by day / outcome / model / transcript. Reads the drainer's cost log and surfaces outlier runs and cache-reuse health (the 41.5M-token failure mode = low cache reuse + high cache writes). Use to answer "what is reflection costing me" for the last day / week.
development
Show fleet status — every claude session running on the host, merged across ainb + claude-peers broker + background jobs. Use when you need to enumerate sessions before composing an action, see which sessions have a peer registered (broker-routable) vs tmux-only, check the `summary` of each session, or pipe the list into jq for filtering. Default output: text table. Pass --format json for LLM consumption.
testing
Ordered multi-step prompts to fleet targets, ack-gated between steps via JSONL assistant-turn-end detection. Use for cycles like disconnect→reconnect→verify, or any flow where step N+1 requires step N to have completed first. The skill BLOCKS until each target's transcript shows the next assistant turn finishing OR per-step timeout fires (default 300s).
development
Center control panel — enumerate every claude session that is blocked waiting on something: a user answer (AskUserQuestion fired), an API error retry, an idle assistant turn-end with no follow-up, or an explicit WAITING: marker. Returns rich JSON with signal kind + context per session. Use this when you've stepped away from the fleet and want one place to see everything that wants your attention and answer it.