UNPKG

memory-engineering-mcp

Version:

๐Ÿง  AI Memory System powered by MongoDB Atlas & Voyage AI - Autonomous memory management with zero manual work

199 lines (192 loc) โ€ข 6.64 kB
/** * Session Context Management v13 - Persistent project binding * SOLVES THE #1 PROBLEM: Context drift and confusion! */ import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs'; import { join, resolve } from 'path'; import { homedir } from 'os'; import { logger } from './logger.js'; import { detectFramework } from './frameworkDetection.js'; const SESSION_FILE = join(homedir(), '.memory-engineering', 'session.json'); /** * Load the current session context */ export function loadSessionContext() { try { if (existsSync(SESSION_FILE)) { const data = JSON.parse(readFileSync(SESSION_FILE, 'utf-8')); // Convert dates back from strings if (data.activeProject?.lastAccessed) { data.activeProject.lastAccessed = new Date(data.activeProject.lastAccessed); } data.history = data.history?.map((h) => ({ ...h, lastAccessed: new Date(h.lastAccessed) })) || []; return data; } } catch (error) { logger.warn('โš ๏ธ Could not load session context, using defaults', error); } return { activeProject: null, history: [], settings: { verbose: false, autoSync: true, showContext: true } }; } /** * Save the session context */ export function saveSessionContext(context) { try { const dir = join(homedir(), '.memory-engineering'); if (!existsSync(dir)) { mkdirSync(dir, { recursive: true }); } writeFileSync(SESSION_FILE, JSON.stringify(context, null, 2)); logger.debug('๐Ÿ’พ Session context saved'); } catch (error) { logger.error('โŒ Failed to save session context', error); } } /** * Set the active project context */ export function setActiveProject(projectPath, projectName) { const resolvedPath = resolve(projectPath); if (!existsSync(resolvedPath)) { throw new Error(`Project path does not exist: ${resolvedPath}`); } const framework = detectFramework(resolvedPath); const name = projectName || resolvedPath.split('/').pop() || 'unnamed'; const context = loadSessionContext(); // Add to history if not already there const historyIndex = context.history.findIndex(h => h.path === resolvedPath); if (historyIndex === -1) { context.history.push({ name, path: resolvedPath, lastAccessed: new Date() }); } else { context.history[historyIndex].lastAccessed = new Date(); } // Keep only last 10 projects in history context.history = context.history .sort((a, b) => b.lastAccessed.getTime() - a.lastAccessed.getTime()) .slice(0, 10); // Set as active context.activeProject = { name, path: resolvedPath, framework, lastAccessed: new Date() }; saveSessionContext(context); logger.info(`๐ŸŽฏ ACTIVE PROJECT SET: ${name}`, { path: resolvedPath, framework: framework.displayName }); return context; } /** * Get the active project or throw an error */ export function getActiveProject() { const context = loadSessionContext(); if (!context.activeProject) { throw new Error(` ๐Ÿ”ด NO ACTIVE PROJECT SET! You MUST set an active project before running any commands: memory_engineering_set_context --projectPath "/path/to/project" Or if you're in a project directory: memory_engineering_set_context --projectPath "." Recent projects: ${context.history.slice(0, 5).map(h => ` โ€ข ${h.name}: ${h.path}`).join('\n')} `); } // Re-detect framework in case it changed context.activeProject.framework = detectFramework(context.activeProject.path); return context.activeProject; } /** * Clear the active project */ export function clearActiveProject() { const context = loadSessionContext(); context.activeProject = null; saveSessionContext(context); logger.info('๐Ÿงน Active project cleared'); } /** * Get a formatted context header for all responses */ export function getContextHeader() { try { const context = loadSessionContext(); if (!context.settings.showContext || !context.activeProject) { return ''; } const timeSinceAccess = Date.now() - new Date(context.activeProject.lastAccessed).getTime(); const hours = Math.floor(timeSinceAccess / (1000 * 60 * 60)); const freshness = hours < 1 ? '๐ŸŸข' : hours < 6 ? '๐ŸŸก' : hours < 24 ? '๐ŸŸ ' : '๐Ÿ”ด'; return ` โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•— โ•‘ ๐ŸŽฏ Active: ${context.activeProject.name.padEnd(20)} ${freshness} ${hours}h ago โ•‘ โ•‘ ๐Ÿ“ Path: ${context.activeProject.path.substring(0, 45).padEnd(45)}โ•‘ โ•‘ ๐Ÿ—๏ธ Stack: ${context.activeProject.framework.displayName.padEnd(20)} โ•‘ โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• `; } catch { return ''; } } /** * Get or infer project path with smart fallbacks */ export function resolveProjectPath(explicitPath) { // 1. Explicit path takes priority if (explicitPath) { return resolve(explicitPath); } // 2. Try active project try { const active = getActiveProject(); if (active) { logger.debug(`๐Ÿ“ Using active project: ${active.name}`); return active.path; } } catch { // No active project } // 3. Try to detect from current directory const cwd = process.cwd(); if (existsSync(join(cwd, 'package.json')) || existsSync(join(cwd, '.git')) || existsSync(join(cwd, '.memory-engineering'))) { logger.debug(`๐Ÿ“ Using current directory as project: ${cwd}`); return cwd; } // 4. Give up and throw helpful error throw new Error(` ๐Ÿ”ด CANNOT DETERMINE PROJECT PATH! No project context found. You have 3 options: 1. Set active project (RECOMMENDED): memory_engineering_set_context --projectPath "/path/to/project" 2. Run from project directory: cd /path/to/project memory_engineering_[command] 3. Specify explicitly: memory_engineering_[command] --projectPath "/path/to/project" `); } //# sourceMappingURL=sessionContext.js.map