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.

123 lines (122 loc) 3.36 kB
import { fileURLToPath as __fileURLToPath } from 'url'; import { dirname as __pathDirname } from 'path'; const __filename = __fileURLToPath(import.meta.url); const __dirname = __pathDirname(__filename); import { existsSync } from "fs"; import { join } from "path"; import { execSync } from "child_process"; import { homedir } from "os"; class DaemonContextService { config; state; intervalId; isRunning = false; onLog; constructor(config, onLog) { this.config = config; this.onLog = onLog; this.state = { lastSaveTime: 0, saveCount: 0, errors: [] }; } start() { if (this.isRunning || !this.config.enabled) { return; } this.isRunning = true; const intervalMs = this.config.interval * 60 * 1e3; this.onLog("INFO", "Context service started", { interval: this.config.interval }); this.saveContext(); this.intervalId = setInterval(() => { this.saveContext(); }, intervalMs); } stop() { if (this.intervalId) { clearInterval(this.intervalId); this.intervalId = void 0; } this.isRunning = false; this.onLog("INFO", "Context service stopped"); } getState() { return { ...this.state }; } updateConfig(config) { const wasRunning = this.isRunning; if (wasRunning) { this.stop(); } this.config = { ...this.config, ...config }; if (wasRunning && this.config.enabled) { this.start(); } } forceSave() { this.saveContext(); } saveContext() { if (!this.isRunning) return; try { const stackmemoryBin = this.getStackMemoryBin(); if (!stackmemoryBin) { this.onLog("WARN", "StackMemory binary not found"); return; } const message = this.config.checkpointMessage || `Auto-checkpoint #${this.state.saveCount + 1}`; const fullMessage = `${message} at ${(/* @__PURE__ */ new Date()).toISOString()}`; execSync(`"${stackmemoryBin}" context add observation "${fullMessage}"`, { timeout: 3e4, encoding: "utf8", stdio: "pipe" }); this.state.saveCount++; this.state.lastSaveTime = Date.now(); this.onLog("INFO", "Context saved", { saveCount: this.state.saveCount }); } catch (err) { const errorMsg = err instanceof Error ? err.message : String(err); if (!errorMsg.includes("EBUSY") && !errorMsg.includes("EAGAIN")) { this.state.errors.push(errorMsg); this.onLog("WARN", "Failed to save context", { error: errorMsg }); if (this.state.errors.length > 10) { this.state.errors = this.state.errors.slice(-10); } } } } getStackMemoryBin() { const homeDir = homedir(); const locations = [ join(homeDir, ".stackmemory", "bin", "stackmemory"), join(homeDir, ".local", "bin", "stackmemory"), "/usr/local/bin/stackmemory", "/opt/homebrew/bin/stackmemory" ]; for (const loc of locations) { if (existsSync(loc)) { return loc; } } try { const result = execSync("which stackmemory", { encoding: "utf8", stdio: "pipe" }).trim(); if (result && existsSync(result)) { return result; } } catch { } return null; } } export { DaemonContextService }; //# sourceMappingURL=context-service.js.map