#!/usr/bin/env bash # session-start.sh - SessionStart hook for claude-handoff plugin # # PURPOSE: # Injects pre-generated handoff content after `/compact handoff:` # completes. Content was generated by PreCompact hook using --fork-session. # # HOOK EVENT: SessionStart (matcher: "compact") # - Fires when sessions continue after compact operations (NOT new sessions!) # - Receives: session_id, transcript_path, cwd, source ("compact") # # BEHAVIOR: # - Checks for handoff content saved by pre-compact.sh # - Injects handoff_content as systemMessage # - Cleans up state file after successful injection # # TESTING: # 1. Enable debug logging in hooks/lib/logging.sh (set LOGGING_ENABLED=true) # 2. Run: /compact handoff:implement feature X # 3. Compact completes, session continues # 4. Check logs: # tail -f /tmp/handoff-sessionstart.log # 5. Verify handoff appears as system message in continuing session # 6. Check state file cleaned up on success: # ls -la .git/handoff-pending/ # should not exist after successful handoff # # MANUAL TESTING WITH FAKE STATE: # # Create fake state with pre-generated content # mkdir -p .git/handoff-pending # echo '{"handoff_content":"## Goal\nTest\n## Context\n- Item 1","goal":"test","trigger":"manual","type":"compact"}' > .git/handoff-pending/handoff-context.json # # # Run hook # echo '{"session_id":"test-123","cwd":"'$(pwd)'","source":"compact"}' | bash session-start.sh # # # Check output contains systemMessage with handoff content # # Cleanup # rm -rf .git/handoff-pending # # EXIT BEHAVIOR: # - Returns JSON with systemMessage if handoff content exists # - Exits silently (exit 0, no output) if no handoff state found # set -euo pipefail # Load logging module source "${BASH_SOURCE%/*}/../lib/logging.sh" init_logging "sessionstart" # Read hook input input=$(cat) session_id=$(echo "$input" | jq -r '.session_id // ""') cwd=$(echo "$input" | jq -r '.cwd // "."') source=$(echo "$input" | jq -r '.source // "unknown"') log "Received input: session_id=$session_id cwd=$cwd source=$source" # Only proceed if this is a compact-triggered session continuation if [[ "$source" != "compact" ]]; then log "Source is '$source', not 'compact'. Exiting." exit 0 fi # Change to project directory cd "$cwd" || exit 0 # Check for pending handoff state state_file=".git/handoff-pending/handoff-context.json" if [[ ! -f "$state_file" ]]; then log "No state file found, exiting" exit 0 fi log "Found state file: $state_file" # Read pre-generated handoff content handoff_content=$(cat "$state_file" | jq -r '.handoff_content // ""') goal=$(cat "$state_file" | jq -r '.goal // ""') log "State: goal='$goal' content_length=${#handoff_content} chars" # If no handoff content, exit without cleanup (might be old state format) if [[ -z "$handoff_content" ]]; then log "No handoff_content in state file, exiting" exit 0 fi # Cleanup: remove both file and directory rm -f "$state_file" rmdir .git/handoff-pending 2>/dev/null || true log "Injecting handoff content as systemMessage" # Return JSON with systemMessage to inject into session jq -n --arg context "$handoff_content" '{ systemMessage: $context }' exit 0