From 182b9a03b10399feb7aa4140b78580f69fd9b1b7 Mon Sep 17 00:00:00 2001 From: Pawel Date: Thu, 16 Apr 2026 18:53:05 +0300 Subject: [PATCH] fix: detect new sessions in existing project dirs + scan local-agent-mode sessions Two session discovery bugs fixed: 1. Cache invalidation: _sessionsNeedRescan() only checked PROJECTS_DIR top-level mtime, so new sessions added to existing project subdirs were invisible until history.jsonl changed or server restarted. Now also tracks each subdir mtime. 2. Claude desktop local-agent-mode: sessions stored in ~/Library/Application Support/Claude/local-agent-mode-sessions/ were never scanned. Added detectLocalAgentModeClaudeDirs() (macOS only) to find embedded .claude dirs and include them in EXTRA_CLAUDE_DIRS. --- src/data.js | 46 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/src/data.js b/src/data.js index 9f1bc76..c9afe83 100644 --- a/src/data.js +++ b/src/data.js @@ -166,8 +166,37 @@ const CURSOR_WORKSPACE_STORAGE = path.join(CURSOR_APP_DATA, 'User', 'workspaceSt const HISTORY_FILE = path.join(CLAUDE_DIR, 'history.jsonl'); const PROJECTS_DIR = path.join(CLAUDE_DIR, 'projects'); +// Scan Claude desktop app's local-agent-mode-sessions for embedded .claude dirs +// Structure: ~/Library/Application Support/Claude/local-agent-mode-sessions///local_/.claude/ +function detectLocalAgentModeClaudeDirs() { + const result = []; + if (process.platform !== 'darwin') return result; + const baseDir = path.join(ALL_HOMES[0], 'Library', 'Application Support', 'Claude', 'local-agent-mode-sessions'); + if (!fs.existsSync(baseDir)) return result; + try { + for (const a of fs.readdirSync(baseDir)) { + const aDir = path.join(baseDir, a); + if (!fs.statSync(aDir).isDirectory()) continue; + for (const b of fs.readdirSync(aDir)) { + const bDir = path.join(aDir, b); + if (!fs.statSync(bDir).isDirectory()) continue; + for (const c of fs.readdirSync(bDir)) { + const cDir = path.join(bDir, c); + if (!fs.statSync(cDir).isDirectory()) continue; + const claudeDir = path.join(cDir, '.claude'); + if (fs.existsSync(claudeDir)) result.push(claudeDir); + } + } + } + } catch {} + return result; +} + // On WSL, collect all alternative data dirs -const EXTRA_CLAUDE_DIRS = ALL_HOMES.slice(1).map(h => path.join(h, '.claude')).filter(d => fs.existsSync(d)); +const EXTRA_CLAUDE_DIRS = [ + ...ALL_HOMES.slice(1).map(h => path.join(h, '.claude')).filter(d => fs.existsSync(d)), + ...detectLocalAgentModeClaudeDirs(), +]; const EXTRA_CODEX_DIRS = ALL_HOMES.slice(1).map(h => path.join(h, '.codex')).filter(d => fs.existsSync(d)); const EXTRA_CURSOR_DIRS = ALL_HOMES.slice(1).map(h => path.join(h, '.cursor')).filter(d => fs.existsSync(d)); @@ -1550,6 +1579,7 @@ const SESSIONS_CACHE_TTL = 60000; // 60 seconds — hot cache, invalidated by fi let _historyMtime = 0; let _historySize = 0; let _projectsDirMtime = 0; +let _projectsSubDirMtimes = {}; // { subDirPath: mtimeMs } function _sessionsNeedRescan() { // Check if history.jsonl or projects dir changed since last scan @@ -1561,6 +1591,14 @@ function _sessionsNeedRescan() { if (fs.existsSync(PROJECTS_DIR)) { const st = fs.statSync(PROJECTS_DIR); if (st.mtimeMs !== _projectsDirMtime) return true; + // Also check subdirectory mtimes — new sessions in existing project dirs + // don't change PROJECTS_DIR mtime, only their parent subdir mtime + for (const entry of fs.readdirSync(PROJECTS_DIR, { withFileTypes: true })) { + if (!entry.isDirectory()) continue; + const subDir = path.join(PROJECTS_DIR, entry.name); + const subSt = fs.statSync(subDir); + if (subSt.mtimeMs !== (_projectsSubDirMtimes[subDir] || 0)) return true; + } } } catch {} return false; @@ -1575,6 +1613,12 @@ function _updateScanMarkers() { } if (fs.existsSync(PROJECTS_DIR)) { _projectsDirMtime = fs.statSync(PROJECTS_DIR).mtimeMs; + _projectsSubDirMtimes = {}; + for (const entry of fs.readdirSync(PROJECTS_DIR, { withFileTypes: true })) { + if (!entry.isDirectory()) continue; + const subDir = path.join(PROJECTS_DIR, entry.name); + try { _projectsSubDirMtimes[subDir] = fs.statSync(subDir).mtimeMs; } catch {} + } } } catch {} }