import { join, dirname, basename, sep } from 'path'; import { homedir } from 'os'; import { existsSync, mkdirSync } from 'fs'; import { execSync } from 'child_process'; import { fileURLToPath } from 'url'; import { loadEarlySetting } from './early-settings.js'; // Get __dirname that works in both ESM (hooks) and CJS (worker) contexts function getDirname(): string { // CJS context - __dirname exists if (typeof __dirname !== 'undefined') { return __dirname; } // ESM context - use import.meta.url return dirname(fileURLToPath(import.meta.url)); } const _dirname = getDirname(); /** * Simple path configuration for claude-mem * Standard paths based on Claude Code conventions */ // Base directories export const DATA_DIR = loadEarlySetting('CLAUDE_MEM_DATA_DIR', join(homedir(), '.claude-mem')); // Note: CLAUDE_CONFIG_DIR is a Claude Code setting, not claude-mem, so leave as env var export const CLAUDE_CONFIG_DIR = process.env.CLAUDE_CONFIG_DIR || join(homedir(), '.claude'); // Data subdirectories export const ARCHIVES_DIR = join(DATA_DIR, 'archives'); export const LOGS_DIR = join(DATA_DIR, 'logs'); export const TRASH_DIR = join(DATA_DIR, 'trash'); export const BACKUPS_DIR = join(DATA_DIR, 'backups'); export const USER_SETTINGS_PATH = join(DATA_DIR, 'settings.json'); export const DB_PATH = join(DATA_DIR, 'claude-mem.db'); export const VECTOR_DB_DIR = join(DATA_DIR, 'vector-db'); // Claude integration paths export const CLAUDE_SETTINGS_PATH = join(CLAUDE_CONFIG_DIR, 'settings.json'); export const CLAUDE_COMMANDS_DIR = join(CLAUDE_CONFIG_DIR, 'commands'); export const CLAUDE_MD_PATH = join(CLAUDE_CONFIG_DIR, 'CLAUDE.md'); /** * Get project-specific archive directory */ export function getProjectArchiveDir(projectName: string): string { return join(ARCHIVES_DIR, projectName); } /** * Get worker socket path for a session */ export function getWorkerSocketPath(sessionId: number): string { return join(DATA_DIR, `worker-${sessionId}.sock`); } /** * Ensure a directory exists */ export function ensureDir(dirPath: string): void { mkdirSync(dirPath, { recursive: true }); } /** * Ensure all data directories exist */ export function ensureAllDataDirs(): void { ensureDir(DATA_DIR); ensureDir(ARCHIVES_DIR); ensureDir(LOGS_DIR); ensureDir(TRASH_DIR); ensureDir(BACKUPS_DIR); } /** * Ensure all Claude integration directories exist */ export function ensureAllClaudeDirs(): void { ensureDir(CLAUDE_CONFIG_DIR); ensureDir(CLAUDE_COMMANDS_DIR); } /** * Get current project name from git root or cwd */ export function getCurrentProjectName(): string { try { const gitRoot = execSync('git rev-parse --show-toplevel', { cwd: process.cwd(), encoding: 'utf8', stdio: ['pipe', 'pipe', 'ignore'] }).trim(); return basename(gitRoot); } catch { return basename(process.cwd()); } } /** * Find package root directory * * Works because bundled hooks are in plugin/scripts/, * so package root is always two levels up */ export function getPackageRoot(): string { return join(_dirname, '..', '..'); } /** * Find commands directory in the installed package */ export function getPackageCommandsDir(): string { const packageRoot = getPackageRoot(); const commandsDir = join(packageRoot, 'commands'); if (!existsSync(join(commandsDir, 'save.md'))) { throw new Error('Package commands directory missing required files'); } return commandsDir; } /** * Create a timestamped backup filename */ export function createBackupFilename(originalPath: string): string { const timestamp = new Date() .toISOString() .replace(/[:.]/g, '-') .replace('T', '_') .slice(0, 19); return `${originalPath}.backup.${timestamp}`; }