diff --git a/install.sh b/install.sh index 1db7f3b..5549f35 100755 --- a/install.sh +++ b/install.sh @@ -190,6 +190,40 @@ fi echo "-> Step 5: Setting up dashboard service..." "$DASHBOARD_SERVICE" install "$PY_PATH" "$INSTALL_DIR" "$MEMORY_DIR" +# -- 5b. Linux: systemd auto-drain (equivalent of macOS LaunchAgent WatchPaths) -- +# On macOS, the reflection LaunchAgent in launchagents/ picks up +# `touch ~/.claude-memory/.reflect-pending` and runs run_reflection.py. +# On Linux there's no LaunchAgent — this block installs a systemd.path + +# oneshot service pair that gives the same behavior via inotify. +if [ "$(uname)" = "Linux" ] && [ -d "$INSTALL_DIR/systemd" ]; then + echo "-> Step 5b: Installing Linux systemd auto-drain unit..." + SYSTEMD_USER_DIR="$HOME/.config/systemd/user" + mkdir -p "$SYSTEMD_USER_DIR" + + for unit in claude-memory-reflection.service claude-memory-reflection.path; do + sed \ + -e "s|@INSTALL_DIR@|$INSTALL_DIR|g" \ + -e "s|@MEMORY_DIR@|$MEMORY_DIR|g" \ + "$INSTALL_DIR/systemd/$unit" > "$SYSTEMD_USER_DIR/$unit" + done + + # Ensure the trigger file exists so systemd.path can watch it from the start + touch "$MEMORY_DIR/.reflect-pending" + + if command -v systemctl &>/dev/null; then + systemctl --user daemon-reload + systemctl --user enable --now claude-memory-reflection.path >/dev/null 2>&1 || { + echo " WARN: could not enable the .path unit via systemctl --user." + echo " Run manually: systemctl --user enable --now claude-memory-reflection.path" + } + if systemctl --user is-active claude-memory-reflection.path >/dev/null 2>&1; then + echo " OK: Reflection auto-drain active (watch: $MEMORY_DIR/.reflect-pending)" + fi + else + echo " WARN: systemctl not found — units copied to $SYSTEMD_USER_DIR but not activated" + fi +fi + # -- 6. Verify -- echo "" echo "-> Step 6: Verifying installation..." diff --git a/systemd/README.md b/systemd/README.md new file mode 100644 index 0000000..8bf4a6a --- /dev/null +++ b/systemd/README.md @@ -0,0 +1,29 @@ +# systemd units (Linux) + +Linux equivalent of the macOS LaunchAgents in `../launchagents/`. + +`install.sh` auto-installs these when running on Linux — it substitutes +`@INSTALL_DIR@` and `@MEMORY_DIR@`, copies the files to +`~/.config/systemd/user/`, and enables them via `systemctl --user`. + +## Units + +- **`claude-memory-reflection.path`** — watches `~/.claude-memory/.reflect-pending` + and fires the service whenever `memory_save` touches the trigger file. +- **`claude-memory-reflection.service`** — `Type=oneshot`, runs + `src/tools/run_reflection.py`, drains `triple_extraction_queue`, + `deep_enrichment_queue`, and `representations_queue` through Ollama. + +## Manual management + +```bash +systemctl --user status claude-memory-reflection.path +systemctl --user status claude-memory-reflection.service +journalctl --user -u claude-memory-reflection.service -f + +# disable auto-drain entirely +systemctl --user disable --now claude-memory-reflection.path + +# run a one-off drain +systemctl --user start claude-memory-reflection.service +``` diff --git a/systemd/claude-memory-reflection.path b/systemd/claude-memory-reflection.path new file mode 100644 index 0000000..63cee9d --- /dev/null +++ b/systemd/claude-memory-reflection.path @@ -0,0 +1,11 @@ +[Unit] +Description=Watch .reflect-pending → trigger reflection drain +Documentation=https://github.com/vbcherepanov/total-agent-memory + +[Path] +# Fires on IN_CLOSE_WRITE — `touch` from memory_save triggers it +PathChanged=@MEMORY_DIR@/.reflect-pending +Unit=claude-memory-reflection.service + +[Install] +WantedBy=default.target diff --git a/systemd/claude-memory-reflection.service b/systemd/claude-memory-reflection.service new file mode 100644 index 0000000..0ab6781 --- /dev/null +++ b/systemd/claude-memory-reflection.service @@ -0,0 +1,15 @@ +[Unit] +Description=Claude Memory — drain reflection queues via Ollama (one-shot) +Documentation=https://github.com/vbcherepanov/total-agent-memory +After=network-online.target + +[Service] +Type=oneshot +WorkingDirectory=@INSTALL_DIR@ +ExecStart=@INSTALL_DIR@/.venv/bin/python @INSTALL_DIR@/src/tools/run_reflection.py +Environment=CLAUDE_MEMORY_DIR=@MEMORY_DIR@ +# Cold Ollama load ~30s first time, then ~10s per queue item +TimeoutStartSec=1200 +Nice=10 +StandardOutput=journal +StandardError=journal