import * as fs from "fs" import * as path from "path" import * as os from "os" const MEMORY_DIR = path.join(os.homedir(), ".llmtune", "memory") const MEMORY_FILES = { preferences: "preferences.md", "project-notes": "project-notes.md", decisions: "decisions.md", architecture: "architecture.md", } as const type MemoryCategory = keyof typeof MEMORY_FILES export interface MemoryEntry { category: MemoryCategory content: string path: string } function ensureMemoryDir(): void { if (!fs.existsSync(MEMORY_DIR)) { fs.mkdirSync(MEMORY_DIR, { recursive: true }) } } function getMemoryPath(category: MemoryCategory): string { return path.join(MEMORY_DIR, MEMORY_FILES[category]) } export function readMemory(category: MemoryCategory): string { const filePath = getMemoryPath(category) try { return fs.readFileSync(filePath, "utf-8").trim() } catch { return "" } } export function writeMemory(category: MemoryCategory, content: string): void { ensureMemoryDir() const filePath = getMemoryPath(category) fs.writeFileSync(filePath, content.trim() + "\n", "utf-8") } export function appendMemory(category: MemoryCategory, line: string): void { ensureMemoryDir() const existing = readMemory(category) const updated = existing ? `${existing}\n${line}` : line writeMemory(category, updated) } export function readAllMemory(): MemoryEntry[] { ensureMemoryDir() const entries: MemoryEntry[] = [] for (const [category, filename] of Object.entries(MEMORY_FILES)) { const content = readMemory(category as MemoryCategory) if (content) { entries.push({ category: category as MemoryCategory, content, path: path.join(MEMORY_DIR, filename), }) } } return entries } export function buildMemoryPrompt(): string { const entries = readAllMemory() if (entries.length === 0) return "" const sections = entries.map((entry) => { const label = entry.category.replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()) return `### ${label}\n${entry.content}` }) return "## User Memory\n\n" + sections.join("\n\n") } export function clearMemory(category?: MemoryCategory): void { if (category) { const filePath = getMemoryPath(category) try { fs.unlinkSync(filePath) } catch { // file doesn't exist } } else { for (const filename of Object.values(MEMORY_FILES)) { try { fs.unlinkSync(path.join(MEMORY_DIR, filename)) } catch { // skip } } } } export function getMemoryDir(): string { return MEMORY_DIR } export function initMemoryFiles(): void { ensureMemoryDir() const defaults: Record = { preferences: "# User Preferences\n# Add your coding preferences here (one per line)\n# Example: I prefer TypeScript over JavaScript\n# Example: I use 2-space indentation\n", "project-notes": "# Project Notes\n# Key facts about the current project\n# Example: Auth uses JWT + bcrypt\n# Example: Database is Neon PostgreSQL via Prisma\n", decisions: "# Architecture Decisions\n# Record important technical decisions\n# Example: Decided to use Prisma instead of Drizzle for ORM\n", architecture: "# Architecture Overview\n# Describe the project structure\n# Example: Frontend: Next.js 16, Backend: Express 5, DB: Neon\n", } for (const [category, defaultContent] of Object.entries(defaults)) { const filePath = getMemoryPath(category as MemoryCategory) if (!fs.existsSync(filePath)) { fs.writeFileSync(filePath, defaultContent, "utf-8") } } }