@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
JavaScript
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