Pulse/scripts/session-handoff.sh
2026-03-18 16:06:30 +00:00

202 lines
4.8 KiB
Bash
Executable file

#!/bin/bash
# session-handoff.sh — Merge a session handoff entry into MEMORY.md under flock.
#
# Usage: scripts/session-handoff.sh <session_id> <handoff_file>
#
# The handoff_file should contain a short markdown snippet (written by Claude)
# with: date, branch, summary, in-progress items, decisions.
#
# This script:
# 1. Acquires flock on a lockfile (safe for parallel sessions)
# 2. Reads the existing MEMORY.md
# 3. Inserts the new entry into the "## Session Handoff Log" section
# 4. Trims to MAX_ENTRIES (oldest dropped)
# 5. Atomic-writes back via temp file + mv
#
# Exit 0 on success, 1 on error.
set -eo pipefail
MAX_ENTRIES=8
SECTION_HEADER="## Session Handoff Log"
ENTRY_DELIM="---"
SESSION_ID="${1:-}"
HANDOFF_FILE="${2:-}"
if [ -z "$SESSION_ID" ] || [ -z "$HANDOFF_FILE" ]; then
echo "Usage: session-handoff.sh <session_id> <handoff_file>" >&2
exit 1
fi
if [ ! -f "$HANDOFF_FILE" ]; then
echo "Error: handoff file not found: $HANDOFF_FILE" >&2
exit 1
fi
# Validate handoff file is not empty
if [ ! -s "$HANDOFF_FILE" ]; then
echo "Error: handoff file is empty" >&2
exit 1
fi
# Resolve memory file path
MEMORY_DIR="$HOME/.claude/projects/-Volumes-Development-pulse-repos-pulse/memory"
MEMORY_FILE="$MEMORY_DIR/MEMORY.md"
LOCK_FILE="$MEMORY_DIR/.handoff.lock"
mkdir -p "$MEMORY_DIR"
# Perform the merge under flock
(
flock -w 10 200 || { echo "Error: could not acquire handoff lock" >&2; exit 1; }
# If MEMORY.md doesn't exist, create a minimal one
if [ ! -f "$MEMORY_FILE" ]; then
echo "# Pulse Development Notes" > "$MEMORY_FILE"
fi
TMPFILE=$(mktemp "$MEMORY_DIR/.memory.XXXXXX")
# Use awk to parse MEMORY.md and merge the new entry.
# Pass the handoff file as a separate awk input (read via FILENAME/NR/FNR).
awk -v section_header="$SECTION_HEADER" \
-v entry_delim="$ENTRY_DELIM" \
-v max_entries="$MAX_ENTRIES" \
-v handoff_file="$HANDOFF_FILE" \
'
BEGIN {
phase = "before"
section_found = 0
before_lines = 0
section_entries = 0
after_lines = 0
current_entry = ""
# Read new entry from handoff file
new_entry = ""
while ((getline line < handoff_file) > 0) {
if (new_entry != "") {
new_entry = new_entry "\n" line
} else {
new_entry = line
}
}
close(handoff_file)
}
phase == "before" && $0 == section_header {
phase = "section"
section_found = 1
next
}
phase == "before" {
before[++before_lines] = $0
next
}
phase == "section" && /^#+ / {
# Hit any markdown heading — switch to after
phase = "after"
after[++after_lines] = $0
next
}
phase == "section" {
# Parse entries delimited by ---
if ($0 == entry_delim) {
if (current_entry != "") {
section_entries++
entries[section_entries] = current_entry
current_entry = ""
}
} else if ($0 != "") {
if (current_entry != "") {
current_entry = current_entry "\n" $0
} else {
current_entry = $0
}
}
next
}
phase == "after" {
after[++after_lines] = $0
next
}
END {
# Capture any trailing entry without final ---
if (current_entry != "") {
section_entries++
entries[section_entries] = current_entry
}
# Helper: emit the handoff section block
# (called at the right position depending on section_found)
# We use a function-like approach via a flag
if (section_found) {
# Section existed — print before, strip trailing blanks
last_content = before_lines
while (last_content > 0 && before[last_content] == "") {
last_content--
}
for (i = 1; i <= last_content; i++) {
print before[i]
}
} else {
# Section did not exist — insert after line 1 (title)
if (before_lines >= 1) {
print before[1]
}
}
# Print the handoff section (exactly one blank line before header)
print ""
print section_header
print ""
print entry_delim
print new_entry
keep = max_entries - 1
printed = 0
for (i = 1; i <= section_entries && printed < keep; i++) {
e = entries[i]
gsub(/^[[:space:]]+|[[:space:]]+$/, "", e)
if (e != "") {
print entry_delim
print entries[i]
printed++
}
}
print entry_delim
if (section_found) {
# Print everything after the section
if (after_lines > 0) {
print ""
}
for (i = 1; i <= after_lines; i++) {
print after[i]
}
} else {
# Print remaining "before" lines (lines 2+) as the rest of the file
print ""
for (i = 2; i <= before_lines; i++) {
print before[i]
}
}
}
' "$MEMORY_FILE" > "$TMPFILE"
mv "$TMPFILE" "$MEMORY_FILE"
) 200>"$LOCK_FILE"
# Clean up the handoff temp file
rm -f "$HANDOFF_FILE"
echo "Handoff entry merged into MEMORY.md (session: $SESSION_ID)"