@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.
137 lines (136 loc) • 3.97 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 { homedir } from "os";
class DaemonLinearService {
config;
state;
intervalId;
isRunning = false;
onLog;
constructor(config, onLog) {
this.config = config;
this.onLog = onLog;
this.state = {
lastSyncTime: 0,
syncCount: 0,
errors: []
};
}
async start() {
if (this.isRunning || !this.config.enabled) {
return;
}
if (!this.isLinearConfigured()) {
this.onLog("WARN", "Linear not configured, skipping linear service");
return;
}
this.isRunning = true;
const intervalMs = this.config.interval * 60 * 1e3;
this.onLog("INFO", "Linear service started", {
interval: this.config.interval,
quietHours: this.config.quietHours
});
await this.performSync();
this.intervalId = setInterval(async () => {
await this.performSync();
}, intervalMs);
}
stop() {
if (this.intervalId) {
clearInterval(this.intervalId);
this.intervalId = void 0;
}
this.isRunning = false;
this.onLog("INFO", "Linear service stopped");
}
getState() {
return {
...this.state,
nextSyncTime: this.isRunning ? this.state.lastSyncTime + this.config.interval * 60 * 1e3 : void 0
};
}
updateConfig(config) {
const wasRunning = this.isRunning;
if (wasRunning) {
this.stop();
}
this.config = { ...this.config, ...config };
if (wasRunning && this.config.enabled) {
this.start();
}
}
async forceSync() {
await this.performSync();
}
async performSync() {
if (!this.isRunning) return;
if (this.isInQuietHours()) {
this.onLog("DEBUG", "Skipping sync during quiet hours");
return;
}
try {
const { LinearAutoSyncService } = await import("../../integrations/linear/auto-sync.js");
const projectRoot = this.findProjectRoot();
if (!projectRoot) {
this.onLog("WARN", "No project root found for Linear sync");
return;
}
const syncService = new LinearAutoSyncService(projectRoot, {
enabled: true,
interval: this.config.interval,
retryAttempts: this.config.retryAttempts,
retryDelay: this.config.retryDelay,
quietHours: this.config.quietHours
});
await syncService.forceSync();
syncService.stop();
this.state.syncCount++;
this.state.lastSyncTime = Date.now();
this.onLog("INFO", "Linear sync completed", {
syncCount: this.state.syncCount
});
} catch (err) {
const errorMsg = err instanceof Error ? err.message : String(err);
this.state.errors.push(errorMsg);
this.onLog("ERROR", "Linear sync failed", { error: errorMsg });
if (this.state.errors.length > 10) {
this.state.errors = this.state.errors.slice(-10);
}
}
}
isLinearConfigured() {
const homeDir = homedir();
const configPath = join(homeDir, ".stackmemory", "linear-auth.json");
return existsSync(configPath) || !!process.env["LINEAR_API_KEY"];
}
findProjectRoot() {
const cwd = process.cwd();
if (existsSync(join(cwd, ".stackmemory"))) {
return cwd;
}
const homeDir = homedir();
if (existsSync(join(homeDir, ".stackmemory"))) {
return homeDir;
}
return null;
}
isInQuietHours() {
if (!this.config.quietHours) return false;
const now = /* @__PURE__ */ new Date();
const currentHour = now.getHours();
const { start, end } = this.config.quietHours;
if (start > end) {
return currentHour >= start || currentHour < end;
} else {
return currentHour >= start && currentHour < end;
}
}
}
export {
DaemonLinearService
};
//# sourceMappingURL=linear-service.js.map