111 lines
3.3 KiB
Python
111 lines
3.3 KiB
Python
#!/usr/bin/env python3
|
|
"""SessionStart hook — staleness check + detached graph rebuild. Fail-open.
|
|
|
|
Must return sub-second; heavy work is detached in the background. Context
|
|
injection is handled by session_context.py (UserPromptSubmit hook).
|
|
"""
|
|
import os
|
|
import sys
|
|
import time
|
|
import subprocess
|
|
from datetime import datetime, timezone
|
|
|
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
import config as config_mod
|
|
import hook_io
|
|
|
|
HOOK_LOG = "/tmp/memory-hook.log"
|
|
|
|
|
|
def _utc():
|
|
return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
|
|
|
|
def _log(line):
|
|
try:
|
|
with open(HOOK_LOG, "a") as f:
|
|
f.write(line + "\n")
|
|
except Exception:
|
|
pass
|
|
|
|
|
|
def _needs_rebuild(stamp, stale_days):
|
|
if not os.path.exists(stamp):
|
|
return True
|
|
try:
|
|
age_days = (time.time() - os.path.getmtime(stamp)) / 86400
|
|
return age_days > stale_days
|
|
except Exception:
|
|
return True
|
|
|
|
|
|
def main():
|
|
inp = hook_io.read_input()
|
|
source = inp.source or "unknown"
|
|
_log(f"[{_utc()}] session-start fired: source={source} cwd={os.getcwd()}")
|
|
|
|
cfg = config_mod.load_config()
|
|
|
|
if os.environ.get("OS_VAULT_SKIP_REBUILD"):
|
|
_log(f"[{_utc()}] session-start: rebuild skipped (OS_VAULT_SKIP_REBUILD set)")
|
|
return
|
|
|
|
cache_dir = os.path.expanduser("~/.cache/graphify")
|
|
stamp = os.path.join(cache_dir, "vault-rebuild.stamp")
|
|
lock = os.path.join(cache_dir, "vault-rebuild.lock")
|
|
logfile = os.path.join(cache_dir, "vault-rebuild.log")
|
|
try:
|
|
os.makedirs(cache_dir, exist_ok=True)
|
|
except Exception:
|
|
pass
|
|
|
|
if _needs_rebuild(stamp, cfg.stale_days):
|
|
if os.path.exists(lock):
|
|
try:
|
|
with open(logfile, "a") as f:
|
|
f.write("[memory/session-start] rebuild already running (lock present), skipping\n")
|
|
except Exception:
|
|
pass
|
|
_log(f"[{_utc()}] session-start: rebuild skipped (lock present)")
|
|
else:
|
|
try:
|
|
open(lock, "a").close()
|
|
except Exception:
|
|
pass
|
|
env = dict(os.environ)
|
|
env.update(cfg.env)
|
|
env["VAULT_PATH"] = cfg.vault_path
|
|
env["MODEL"] = cfg.model
|
|
env["STAMP"] = stamp
|
|
env["LOCK"] = lock
|
|
env["LOGFILE"] = logfile
|
|
cmd = (
|
|
'graphify extract "$VAULT_PATH" --backend ollama --model "$MODEL" --force '
|
|
'>> "$LOGFILE" 2>&1 '
|
|
'&& date > "$STAMP" && rm -f "$LOCK" '
|
|
'|| (rm -f "$LOCK"; echo "rebuild failed" >> "$LOGFILE")'
|
|
)
|
|
try:
|
|
devnull = open(os.devnull, "wb")
|
|
subprocess.Popen(
|
|
["bash", "-c", cmd],
|
|
stdin=subprocess.DEVNULL,
|
|
stdout=devnull,
|
|
stderr=devnull,
|
|
env=env,
|
|
start_new_session=True,
|
|
)
|
|
except Exception:
|
|
pass
|
|
_log(f"[{_utc()}] session-start: rebuild triggered")
|
|
else:
|
|
_log(f"[{_utc()}] session-start: rebuild not needed")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
try:
|
|
main()
|
|
except Exception as e:
|
|
print(f"[session-start] error: {e}", file=sys.stderr)
|
|
sys.exit(0)
|