From 669f9255a2ba8f43dca35420f992994175d0b68c Mon Sep 17 00:00:00 2001 From: Levi Neely Date: Sat, 14 Mar 2026 15:10:58 +0100 Subject: [PATCH] mcptools: add bootstrap_session.sh for session handoff Assembles a bootstrap prompt from a bead: full bead JSON fields, comments, recent git log (from mount cwd), and any denote documents referenced in description/notes/comments. Crash-resilient: state lives in beads + git, no agent snapshot needed. Documents handoff pattern in anvillm-sessions SKILL.md. Co-Authored-By: Claude Sonnet 4.6 --- mcptools/bootstrap_session.sh | 115 +++++++++++++++++++++++++++++++ skills/anvillm-sessions/SKILL.md | 18 +++++ 2 files changed, 133 insertions(+) create mode 100755 mcptools/bootstrap_session.sh diff --git a/mcptools/bootstrap_session.sh b/mcptools/bootstrap_session.sh new file mode 100755 index 0000000..3cc33b6 --- /dev/null +++ b/mcptools/bootstrap_session.sh @@ -0,0 +1,115 @@ +#!/bin/bash +# capabilities: beads, agents +# description: Build a bootstrap prompt for session continuity from a bead +# Usage: bootstrap_session.sh --mount --id [--git-lines ] +set -euo pipefail + +if cat /etc/shadow >/dev/null 2>&1; then + echo "Error: This script must be run via execute_code tool" >&2 + exit 1 +fi + +MOUNT="" +BEAD_ID="" +GIT_LINES=30 + +while [[ $# -gt 0 ]]; do + case "$1" in + --mount) MOUNT="$2"; shift 2 ;; + --id) BEAD_ID="$2"; shift 2 ;; + --git-lines) GIT_LINES="$2"; shift 2 ;; + *) echo "unknown argument: $1" >&2; exit 1 ;; + esac +done + +if [ -z "$MOUNT" ] || [ -z "$BEAD_ID" ]; then + echo "usage: bootstrap_session.sh --mount --id [--git-lines ]" >&2 + exit 1 +fi + +# Get bead JSON +BEAD_JSON=$(9p read "anvillm/beads/$MOUNT/$BEAD_ID/json" 2>/dev/null) || { + echo "error: bead $BEAD_ID not found in mount $MOUNT" >&2 + exit 1 +} + +# Extract fields +TITLE=$(echo "$BEAD_JSON" | jq -r '.title // ""') +STATUS=$(echo "$BEAD_JSON" | jq -r '.status // ""') +DESCRIPTION=$(echo "$BEAD_JSON" | jq -r '.description // ""') +ACCEPTANCE=$(echo "$BEAD_JSON" | jq -r '.acceptance_criteria // ""') +NOTES=$(echo "$BEAD_JSON" | jq -r '.notes // ""') +ASSIGNEE=$(echo "$BEAD_JSON" | jq -r '.assignee // ""') +COMMENT_COUNT=$(echo "$BEAD_JSON" | jq -r '.comment_count // 0') +PARENT_ID=$(echo "$BEAD_JSON" | jq -r '.parent_id // ""') + +# Get comments if any +COMMENTS="" +if [ "$COMMENT_COUNT" -gt 0 ]; then + COMMENTS=$(9p read "anvillm/beads/$MOUNT/$BEAD_ID/comments" 2>/dev/null \ + | jq -r '.[] | "[\(.created_at | split("T")[0])] \(.author // "agent"): \(.text)"' 2>/dev/null || true) +fi + +# Get mount cwd from mtab +CWD=$(9p read anvillm/beads/mtab 2>/dev/null | awk -v m="$MOUNT" -F'\t' '$1 == m {print $2}') + +# Get git log if we have a cwd +GIT_LOG="" +if [ -n "$CWD" ] && [ -d "$CWD/.git" ]; then + GIT_LOG=$(git -C "$CWD" log --oneline -n "$GIT_LINES" 2>/dev/null || true) +fi + +# Collect denote references from description, notes, and comments +ALL_TEXT="$DESCRIPTION $NOTES $COMMENTS" +DENOTE_IDS=$(echo "$ALL_TEXT" | grep -oE 'denote:[0-9]{8}T[0-9]{6}' | sed 's/denote://' | sort -u || true) + +# Output bootstrap prompt +cat </dev/null | head -1) + if [ -n "$FILE" ]; then + echo "" + echo "#### denote:$did ($(basename "$FILE"))" + echo '```' + cat "$FILE" + echo '```' + fi + done <<< "$DENOTE_IDS" +fi diff --git a/skills/anvillm-sessions/SKILL.md b/skills/anvillm-sessions/SKILL.md index 9e60658..f3660b8 100644 --- a/skills/anvillm-sessions/SKILL.md +++ b/skills/anvillm-sessions/SKILL.md @@ -46,6 +46,14 @@ tool: kill_agent.sh args: ["--agent-id", ""] ``` +Bootstrap session (build context prompt from a bead for handoff): +``` +Tool: execute_code +tool: bootstrap_session.sh +args: ["--mount", "", "--id", ""] +``` +Optional: `"--git-lines", ""` (default 30). Output includes bead details, comments, recent git log, and any referenced denote documents. Feed the output as the initial prompt when spawning a replacement agent. + ## Backends - `kiro-cli` - Kiro CLI agent @@ -74,6 +82,16 @@ args: ["--agent-id", ""] - Need to stop/restart/kill an agent - Coordinating multi-agent workflows +## Session Handoff Pattern + +Agent A finishes or is killed. Agent B picks up where A left off: + +1. Agent B (or supervisor) calls `bootstrap_session.sh --mount --id ` +2. Feed the output as B's initial prompt (via `--prompt` in `spawn_agent.sh`, or as the first message in `context`) +3. B reads the bead, claims it, and resumes work + +Bootstrap is crash-resilient: state lives in beads + git, not in the agent's memory. No agent-written snapshot required. + ## When NOT to Use - Sending messages to agents (use anvillm-communication skill)