@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.
150 lines (149 loc) • 4.13 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, mkdirSync, readFileSync, writeFileSync } from "fs";
import { join } from "path";
import { homedir } from "os";
const DEFAULT_DAEMON_CONFIG = {
version: "1.0.0",
context: {
enabled: true,
interval: 15,
// 15 minutes
checkpointMessage: "Auto-checkpoint"
},
linear: {
enabled: false,
// Disabled by default, requires setup
interval: 60,
// 60 minutes
quietHours: { start: 22, end: 7 },
retryAttempts: 3,
retryDelay: 3e4
},
fileWatch: {
enabled: false,
// Disabled by default
interval: 0,
// Not interval-based
paths: ["."],
extensions: [".ts", ".js", ".tsx", ".jsx", ".py", ".go", ".rs"],
ignore: ["node_modules", ".git", "dist", "build", ".stackmemory"],
debounceMs: 2e3
},
heartbeatInterval: 60,
// 1 minute
inactivityTimeout: 0,
// Disabled by default
logLevel: "info"
};
function getDaemonDir() {
const dir = join(homedir(), ".stackmemory", "daemon");
if (!existsSync(dir)) {
mkdirSync(dir, { recursive: true });
}
return dir;
}
function getLogsDir() {
const dir = join(homedir(), ".stackmemory", "logs");
if (!existsSync(dir)) {
mkdirSync(dir, { recursive: true });
}
return dir;
}
function getDaemonPaths() {
const daemonDir = getDaemonDir();
const logsDir = getLogsDir();
return {
pidFile: join(daemonDir, "daemon.pid"),
statusFile: join(daemonDir, "daemon.status"),
configFile: join(daemonDir, "config.json"),
logFile: join(logsDir, "daemon.log")
};
}
function loadDaemonConfig() {
const { configFile } = getDaemonPaths();
if (!existsSync(configFile)) {
return { ...DEFAULT_DAEMON_CONFIG };
}
try {
const content = readFileSync(configFile, "utf8");
const config = JSON.parse(content);
return {
...DEFAULT_DAEMON_CONFIG,
...config,
context: { ...DEFAULT_DAEMON_CONFIG.context, ...config.context },
linear: { ...DEFAULT_DAEMON_CONFIG.linear, ...config.linear },
fileWatch: { ...DEFAULT_DAEMON_CONFIG.fileWatch, ...config.fileWatch }
};
} catch {
return { ...DEFAULT_DAEMON_CONFIG };
}
}
function saveDaemonConfig(config) {
const { configFile } = getDaemonPaths();
const currentConfig = loadDaemonConfig();
const newConfig = {
...currentConfig,
...config,
context: { ...currentConfig.context, ...config.context },
linear: { ...currentConfig.linear, ...config.linear },
fileWatch: { ...currentConfig.fileWatch, ...config.fileWatch }
};
writeFileSync(configFile, JSON.stringify(newConfig, null, 2));
}
function readDaemonStatus() {
const { statusFile, pidFile } = getDaemonPaths();
const defaultStatus = {
running: false,
services: {
context: { enabled: false },
linear: { enabled: false },
fileWatch: { enabled: false }
},
errors: []
};
if (!existsSync(pidFile)) {
return defaultStatus;
}
try {
const pidContent = readFileSync(pidFile, "utf8").trim();
const pid = parseInt(pidContent, 10);
try {
process.kill(pid, 0);
} catch {
return defaultStatus;
}
if (!existsSync(statusFile)) {
return { ...defaultStatus, running: true, pid };
}
const content = readFileSync(statusFile, "utf8");
const status = JSON.parse(content);
return {
...status,
running: true,
pid,
uptime: status.startTime ? Date.now() - status.startTime : void 0
};
} catch {
return defaultStatus;
}
}
function writeDaemonStatus(status) {
const { statusFile } = getDaemonPaths();
const currentStatus = readDaemonStatus();
const newStatus = { ...currentStatus, ...status };
writeFileSync(statusFile, JSON.stringify(newStatus, null, 2));
}
export {
DEFAULT_DAEMON_CONFIG,
getDaemonDir,
getDaemonPaths,
getLogsDir,
loadDaemonConfig,
readDaemonStatus,
saveDaemonConfig,
writeDaemonStatus
};
//# sourceMappingURL=daemon-config.js.map