#!/bin/bash # # Periodic Context Save Hook # Runs as a background daemon to save context every 5 minutes of active time # # Usage: bash .claude/hooks/periodic-context-save start # bash .claude/hooks/periodic-context-save stop # bash .claude/hooks/periodic-context-save status # SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" CLAUDE_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" PID_FILE="$CLAUDE_DIR/.periodic-save.pid" STATE_FILE="$CLAUDE_DIR/.periodic-save-state" CONFIG_FILE="$CLAUDE_DIR/context-recall-config.env" # Load configuration if [ -f "$CONFIG_FILE" ]; then source "$CONFIG_FILE" fi # Configuration SAVE_INTERVAL_SECONDS=300 # 5 minutes CHECK_INTERVAL_SECONDS=60 # Check every minute API_URL="${CLAUDE_API_URL:-http://172.16.3.30:8001}" # Detect project ID detect_project_id() { # Try git config first PROJECT_ID=$(git config --local claude.projectid 2>/dev/null) if [ -z "$PROJECT_ID" ]; then # Try to derive from git remote URL GIT_REMOTE=$(git config --get remote.origin.url 2>/dev/null) if [ -n "$GIT_REMOTE" ]; then PROJECT_ID=$(echo -n "$GIT_REMOTE" | md5sum | cut -d' ' -f1) fi fi echo "$PROJECT_ID" } # Check if Claude Code is active (not idle) is_claude_active() { # Check if there are recent Claude Code processes or activity # This is a simple heuristic - can be improved # On Windows with Git Bash, check for claude process if command -v tasklist.exe >/dev/null 2>&1; then tasklist.exe 2>/dev/null | grep -i claude >/dev/null 2>&1 return $? fi # Assume active if we can't detect return 0 } # Get active time from state file get_active_time() { if [ -f "$STATE_FILE" ]; then cat "$STATE_FILE" | grep "^active_seconds=" | cut -d'=' -f2 else echo "0" fi } # Update active time in state file update_active_time() { local active_seconds=$1 echo "active_seconds=$active_seconds" > "$STATE_FILE" echo "last_update=$(date -u +"%Y-%m-%dT%H:%M:%SZ")" >> "$STATE_FILE" } # Save context to database save_periodic_context() { local project_id=$(detect_project_id) # Generate context summary local title="Periodic Save - $(date +"%Y-%m-%d %H:%M")" local summary="Auto-saved context after 5 minutes of active work. Session in progress on project: ${project_id:-unknown}" # Create JSON payload local payload=$(cat </dev/null 2>&1 if [ $? -eq 0 ]; then echo "[$(date)] Context saved successfully" >&2 else echo "[$(date)] Failed to save context" >&2 fi else echo "[$(date)] No JWT token - cannot save context" >&2 fi } # Main monitoring loop monitor_loop() { local active_seconds=0 echo "[$(date)] Periodic context save daemon started (PID: $$)" >&2 echo "[$(date)] Will save context every ${SAVE_INTERVAL_SECONDS}s of active time" >&2 while true; do # Check if Claude is active if is_claude_active; then # Increment active time active_seconds=$((active_seconds + CHECK_INTERVAL_SECONDS)) update_active_time $active_seconds # Check if we've reached the save interval if [ $active_seconds -ge $SAVE_INTERVAL_SECONDS ]; then echo "[$(date)] ${SAVE_INTERVAL_SECONDS}s of active time reached - saving context" >&2 save_periodic_context # Reset timer active_seconds=0 update_active_time 0 fi else echo "[$(date)] Claude Code inactive - not counting time" >&2 fi # Wait before next check sleep $CHECK_INTERVAL_SECONDS done } # Start daemon start_daemon() { if [ -f "$PID_FILE" ]; then local pid=$(cat "$PID_FILE") if kill -0 $pid 2>/dev/null; then echo "Periodic context save daemon already running (PID: $pid)" return 1 fi fi # Start in background nohup bash "$0" _monitor >> "$CLAUDE_DIR/periodic-save.log" 2>&1 & local pid=$! echo $pid > "$PID_FILE" echo "Started periodic context save daemon (PID: $pid)" echo "Logs: $CLAUDE_DIR/periodic-save.log" } # Stop daemon stop_daemon() { if [ ! -f "$PID_FILE" ]; then echo "Periodic context save daemon not running" return 1 fi local pid=$(cat "$PID_FILE") if kill $pid 2>/dev/null; then echo "Stopped periodic context save daemon (PID: $pid)" rm -f "$PID_FILE" rm -f "$STATE_FILE" else echo "Failed to stop daemon (PID: $pid) - may not be running" rm -f "$PID_FILE" fi } # Check status check_status() { if [ -f "$PID_FILE" ]; then local pid=$(cat "$PID_FILE") if kill -0 $pid 2>/dev/null; then local active_seconds=$(get_active_time) echo "Periodic context save daemon is running (PID: $pid)" echo "Active time: ${active_seconds}s / ${SAVE_INTERVAL_SECONDS}s" return 0 else echo "Daemon PID file exists but process not running" rm -f "$PID_FILE" return 1 fi else echo "Periodic context save daemon not running" return 1 fi } # Command dispatcher case "$1" in start) start_daemon ;; stop) stop_daemon ;; status) check_status ;; _monitor) # Internal command - run monitor loop monitor_loop ;; *) echo "Usage: $0 {start|stop|status}" echo "" echo "Periodic context save daemon - saves context every 5 minutes of active time" echo "" echo "Commands:" echo " start - Start the background daemon" echo " stop - Stop the daemon" echo " status - Check daemon status" exit 1 ;; esac