UNPKG

@stackmemoryai/stackmemory

Version:

Project-scoped memory for AI coding tools. Durable context across sessions with MCP integration, frames, smart retrieval, Claude Code skills, and automatic hooks.

88 lines (87 loc) 2.64 kB
import { fileURLToPath as __fileURLToPath } from 'url'; import { dirname as __pathDirname } from 'path'; const __filename = __fileURLToPath(import.meta.url); const __dirname = __pathDirname(__filename); import { EventEmitter } from "events"; import { watch, readFileSync, existsSync } from "fs"; import { join } from "path"; const HOME = process.env["HOME"] || "/tmp"; const DEFAULT_STATE_FILE = join(HOME, ".stackmemory", "sweep-state.json"); class SweepStateWatcher extends EventEmitter { stateFile; lastPredictionTs = 0; lastPendingTs = 0; watcher = null; debounceTimer = null; pollTimer = null; constructor(stateFile) { super(); this.stateFile = stateFile || DEFAULT_STATE_FILE; } start() { if (this.watcher) return; if (!existsSync(this.stateFile)) { this.pollTimer = setInterval(() => { if (existsSync(this.stateFile)) { clearInterval(this.pollTimer); this.pollTimer = null; this.startWatching(); } }, 1e3); return; } this.startWatching(); } startWatching() { this.readState(); this.watcher = watch(this.stateFile, () => { if (this.debounceTimer) clearTimeout(this.debounceTimer); this.debounceTimer = setTimeout(() => this.readState(), 100); }); this.watcher.on("error", () => { this.watcher?.close(); this.watcher = null; setTimeout(() => this.start(), 1e3); }); } readState() { try { if (!existsSync(this.stateFile)) return; const raw = readFileSync(this.stateFile, "utf-8"); const state = JSON.parse(raw); if (state.pendingPrediction && state.pendingPrediction !== this.lastPendingTs) { this.lastPendingTs = state.pendingPrediction; this.emit("loading"); } if (state.lastPrediction && state.lastPrediction.timestamp > this.lastPredictionTs) { this.lastPredictionTs = state.lastPrediction.timestamp; const event = { file_path: state.lastPrediction.file_path, prediction: state.lastPrediction.prediction, latency_ms: state.lastPrediction.latency_ms, timestamp: state.lastPrediction.timestamp }; this.emit("prediction", event); } } catch { } } stop() { if (this.pollTimer) { clearInterval(this.pollTimer); this.pollTimer = null; } if (this.debounceTimer) { clearTimeout(this.debounceTimer); this.debounceTimer = null; } if (this.watcher) { this.watcher.close(); this.watcher = null; } } } export { SweepStateWatcher }; //# sourceMappingURL=state-watcher.js.map