UNPKG

mira-consciousness

Version:

Memory & Intelligence Retention Archive - Preserving The Spark

207 lines 8.61 kB
/** * Cross-Platform Path Utilities * ============================= * * Centralized utilities for handling paths in a cross-platform manner. * This module provides the single source of truth for all path operations * to ensure MIRA works correctly on Windows, macOS, Linux, and containers. * * Key Features: * - Intelligent Claude conversation path discovery * - Cross-platform temp directory handling * - Safe path joining and normalization * - Platform-specific path detection and conversion */ import * as path from 'path'; import * as os from 'os'; import fs from 'fs-extra'; /** * Get Claude conversation paths to search in order of likelihood * This matches the Python claude_path_discovery.py logic */ export function getClaudeConversationSearchPaths() { const homeDir = os.homedir(); const currentDir = process.cwd(); const searchPaths = [ // TIER 1: Standard user locations (90% of installations) path.join(homeDir, '.claude', 'projects'), path.join(homeDir, '.claude-code', 'projects'), // TIER 2: Development environments (common in professional setups) // Use path.join instead of hardcoded paths for cross-platform compatibility ...(process.platform !== 'win32' ? [ path.join(path.sep, 'home', 'codespace', '.claude', 'projects'), path.join(path.sep, 'home', 'codespace', '.claude-code', 'projects'), path.join(path.sep, 'home', 'vscode', '.claude', 'projects'), path.join(path.sep, 'home', 'gitpod', '.claude', 'projects'), ] : []), // TIER 3: Current workspace variations (local development) path.join(currentDir, '.claude', 'projects'), path.join(currentDir, '..', '.claude', 'projects'), path.join(currentDir, 'claude', 'projects'), // TIER 4: Platform-specific standard locations // Windows ...(process.platform === 'win32' ? [ path.join(homeDir, 'AppData', 'Local', 'Claude', 'projects'), path.join(homeDir, 'AppData', 'Local', 'claude-code', 'projects'), path.join(homeDir, 'AppData', 'Roaming', 'Claude', 'projects'), ] : []), // macOS ...(process.platform === 'darwin' ? [ path.join(homeDir, 'Library', 'Application Support', 'Claude', 'projects'), path.join(homeDir, 'Library', 'Application Support', 'claude-code', 'projects'), path.join(homeDir, 'Library', 'Caches', 'Claude', 'projects'), ] : []), // Linux standard directories ...(process.platform === 'linux' ? [ path.join(homeDir, '.local', 'share', 'claude', 'projects'), path.join(homeDir, '.config', 'claude', 'projects'), path.join(homeDir, '.cache', 'claude', 'projects'), path.join(homeDir, 'snap', 'claude', 'common', 'projects'), ] : []), // TIER 5: Container and virtualization environments ...(process.platform !== 'win32' ? [ path.join(path.sep, 'workspace', '.claude', 'projects'), path.join(path.sep, 'app', '.claude', 'projects'), path.join(path.sep, 'opt', '.claude', 'projects'), path.join(path.sep, 'usr', 'local', 'share', 'claude', 'projects'), path.join(path.sep, 'var', 'lib', 'claude', 'projects'), ] : []), // TIER 6: WSL (Windows Subsystem for Linux) paths ...(process.platform === 'win32' || fs.existsSync('/mnt/c') ? [ path.join(path.sep, 'mnt', 'c', 'Users', process.env.USERNAME || 'user', '.claude', 'projects'), path.join(path.sep, 'mnt', 'c', 'Users', process.env.USER || 'user', '.claude', 'projects'), ] : []), // TIER 7: Alternative environment variables and dynamic paths ...(process.env.CLAUDE_HOME ? [path.join(process.env.CLAUDE_HOME, 'projects')] : []), ...(process.env.CLAUDE_DATA_DIR ? [path.join(process.env.CLAUDE_DATA_DIR, 'projects')] : []), ...(process.env.XDG_DATA_HOME ? [path.join(process.env.XDG_DATA_HOME, 'claude', 'projects')] : []), ...(process.env.XDG_CONFIG_HOME ? [path.join(process.env.XDG_CONFIG_HOME, 'claude', 'projects')] : []), ]; // Use temp directory as last resort (cross-platform) searchPaths.push(path.join(os.tmpdir(), '.claude', 'projects')); // Remove duplicates and return return [...new Set(searchPaths)]; } /** * Validate that a path contains Claude conversation files */ export async function validateClaudeConversationPath(searchPath) { try { if (!await fs.pathExists(searchPath)) { return false; } const stats = await fs.stat(searchPath); if (!stats.isDirectory()) { return false; } // Check for project directories containing .jsonl files const entries = await fs.readdir(searchPath); for (const entry of entries) { const projectPath = path.join(searchPath, entry); const projectStats = await fs.stat(projectPath); if (projectStats.isDirectory()) { const projectFiles = await fs.readdir(projectPath); const hasJsonl = projectFiles.some(file => file.endsWith('.jsonl')); if (hasJsonl) { // Try to validate at least one conversation file const jsonlFile = projectFiles.find(f => f.endsWith('.jsonl')); if (jsonlFile) { const filePath = path.join(projectPath, jsonlFile); const content = await fs.readFile(filePath, 'utf-8'); const lines = content.split('\n').filter(l => l.trim()); // Check first few lines for Claude conversation format for (let i = 0; i < Math.min(5, lines.length); i++) { try { const data = JSON.parse(lines[i]); if (data.message && (data.message.role === 'user' || data.message.role === 'assistant')) { return true; // Valid Claude conversation found } } catch { // Continue checking other lines } } } } } } return false; } catch (error) { return false; } } /** * Get cross-platform temp directory * Handles Windows vs Unix differences */ export function getCrossPlatformTempDir() { return os.tmpdir(); } /** * Convert Windows-style paths to cross-platform paths */ export function normalizePath(inputPath) { // Convert backslashes to forward slashes let normalized = inputPath.replace(/\\/g, path.sep); // Handle Windows drive letters if (process.platform !== 'win32' && /^[A-Za-z]:/.test(normalized)) { // Convert C:\path to /mnt/c/path for WSL const driveLetter = normalized[0].toLowerCase(); normalized = `/mnt/${driveLetter}${normalized.substring(2)}`; } return path.normalize(normalized); } /** * Check if a path is absolute in a cross-platform way */ export function isAbsolutePath(inputPath) { return path.isAbsolute(inputPath) || /^[A-Za-z]:/.test(inputPath) || // Windows drive letter inputPath.startsWith('/'); // Unix absolute } /** * Join paths safely across platforms */ export function safejoin(...paths) { return path.join(...paths); } /** * Get platform-specific null device */ export function getNullDevice() { return process.platform === 'win32' ? 'nul' : '/dev/null'; } /** * Get home directory with fallback */ export function getHomeDirectory() { return os.homedir() || process.env.HOME || process.env.USERPROFILE || '.'; } /** * Check if running in WSL */ export function isWSL() { if (process.platform !== 'linux') return false; try { return fs.existsSync('/proc/version') && fs.readFileSync('/proc/version', 'utf8').toLowerCase().includes('microsoft'); } catch { return false; } } /** * Get platform name for logging */ export function getPlatformName() { switch (process.platform) { case 'win32': return 'Windows'; case 'darwin': return 'macOS'; case 'linux': return isWSL() ? 'WSL' : 'Linux'; default: return process.platform; } } //# sourceMappingURL=crossPlatformPaths.js.map