cc-os/plugins/memory/hooks/session-start.sh

106 lines
4.6 KiB
Bash
Executable File

#!/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 - <<PY 2>/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 >/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