#!/usr/bin/env bash # SessionStart hook for the memory plugin. # Invoked by Claude Code at the start of every session with JSON on stdin. # Must return sub-second; heavy work is detached in the background. # Context injection is handled by session-context.sh (UserPromptSubmit hook). # Never let any individual failure exit the whole script. # Do NOT set -e — grep-no-match, graphify errors, etc. are all expected. # --------------------------------------------------------------------------- # 0. Read stdin (ignore errors — fields may be absent on older Claude Code) # --------------------------------------------------------------------------- STDIN_JSON="$(cat 2>/dev/null || true)" HOOK_SOURCE="$(printf '%s' "$STDIN_JSON" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('source','unknown'))" 2>/dev/null || true)" echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] session-start fired: source=${HOOK_SOURCE:-unknown} cwd=$(pwd)" >> /tmp/memory-hook.log 2>/dev/null || true CWD="$(printf '%s' "$STDIN_JSON" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('cwd',''))" 2>/dev/null || true)" CWD="${CWD:-$PWD}" # --------------------------------------------------------------------------- # 1. Parse config.yaml — emit shell variable assignments, eval them in. # python3 + PyYAML are guaranteed present via graphify. # --------------------------------------------------------------------------- eval "$(python3 - <<'PY' import yaml, os, shlex, sys CONFIG_PATH = os.path.expanduser("~/.claude/plugins/memory/config.yaml") try: with open(CONFIG_PATH) as f: cfg = yaml.safe_load(f) or {} except Exception: cfg = {} out = { "VAULT_PATH": os.path.expanduser(cfg.get("vault_path", "~/Documents/SecondBrain")), "GRAPHIFY_OUTPUT_DIR": os.path.expanduser(cfg.get("graphify_output_dir", "~/Documents/SecondBrain/.graphify")), "MODEL": cfg.get("ollama_model", "qwen25-coder-7b-16k"), "STALE_DAYS": str(cfg.get("stale_threshold_days", 7)), } for k, v in out.items(): print(f"{k}={shlex.quote(str(v))}") # Export env block so the background rebuild process inherits them. for k, v in (cfg.get("env") or {}).items(): print(f"export {k}={shlex.quote(str(v))}") PY 2>/dev/null || true)" # Apply fallbacks in case the python3 block produced nothing. VAULT_PATH="${VAULT_PATH:-$HOME/Documents/SecondBrain}" GRAPHIFY_OUTPUT_DIR="${GRAPHIFY_OUTPUT_DIR:-$HOME/Documents/SecondBrain/.graphify}" MODEL="${MODEL:-qwen25-coder-7b-16k}" STALE_DAYS="${STALE_DAYS:-7}" # --------------------------------------------------------------------------- # 2. Cache dir + file paths # --------------------------------------------------------------------------- CACHE_DIR="$HOME/.cache/graphify" STAMP="$CACHE_DIR/vault-rebuild.stamp" LOCK="$CACHE_DIR/vault-rebuild.lock" LOGFILE="$CACHE_DIR/vault-rebuild.log" mkdir -p "$CACHE_DIR" 2>/dev/null || true # Export vars needed by the detached bash -c subprocess. export VAULT_PATH MODEL STAMP LOCK LOGFILE # --------------------------------------------------------------------------- # 3. Staleness check + detached background rebuild # --------------------------------------------------------------------------- _needs_rebuild() { [ ! -f "$STAMP" ] && return 0 # Use python3 for portable mtime arithmetic (avoids `date -d` vs BSD split). python3 - </dev/null import os, time stamp = "$STAMP" stale_days = int("$STALE_DAYS") try: age_days = (time.time() - os.path.getmtime(stamp)) / 86400 exit(0 if age_days > stale_days else 1) except Exception: exit(0) PY } if _needs_rebuild; then if [ -f "$LOCK" ]; then echo "[memory/session-start] rebuild already running (lock present), skipping" >> "$LOGFILE" 2>/dev/null || true echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] session-start: rebuild skipped (lock present)" >> /tmp/memory-hook.log 2>/dev/null || true else # Create lock, then spawn fully detached (no inherited stdin/stdout/stderr). touch "$LOCK" 2>/dev/null || true # shellcheck disable=SC2016 nohup bash -c \ 'graphify extract "$VAULT_PATH" --backend ollama --model "$MODEL" --force \ >> "$LOGFILE" 2>&1 \ && date > "$STAMP" && rm -f "$LOCK" \ || (rm -f "$LOCK"; echo "rebuild failed" >> "$LOGFILE")' \ /dev/null 2>&1 & echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] session-start: rebuild triggered" >> /tmp/memory-hook.log 2>/dev/null || true fi else echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] session-start: rebuild not needed" >> /tmp/memory-hook.log 2>/dev/null || true fi exit 0