()
| 280 | } |
| 281 | |
| 282 | function startSwitchWatcher() { |
| 283 | if (state.switchWatcher) { clearInterval(state.switchWatcher); state.switchWatcher = null; } |
| 284 | const slug = getProjectSlug(state.CWD); |
| 285 | const projectDir = path.join(PROJECTS_DIR, slug); |
| 286 | |
| 287 | state.switchWatcher = setInterval(() => { |
| 288 | if (!state.transcriptPath || !state.expectingSwitch || !fs.existsSync(projectDir)) return; |
| 289 | try { |
| 290 | const currentBasename = path.basename(state.transcriptPath); |
| 291 | const candidates = fs.readdirSync(projectDir) |
| 292 | .filter(f => f.endsWith('.jsonl') && f !== currentBasename) |
| 293 | .map(f => { |
| 294 | const full = path.join(projectDir, f); |
| 295 | const stat = fs.statSync(full); |
| 296 | return { name: f, full, mtime: stat.mtimeMs, size: stat.size }; |
| 297 | }) |
| 298 | .filter(t => t.mtime > fs.statSync(state.transcriptPath).mtimeMs) |
| 299 | .sort((a, b) => b.mtime - a.mtime); |
| 300 | |
| 301 | const newer = candidates.find(t => fileLooksLikeTranscript(t.full)); |
| 302 | if (newer) { |
| 303 | log(`Session switch detected → ${path.basename(newer.full, '.jsonl')}`); |
| 304 | state.expectingSwitch = false; |
| 305 | if (state.expectingSwitchTimer) { clearTimeout(state.expectingSwitchTimer); state.expectingSwitchTimer = null; } |
| 306 | if (state.tailTimer) { clearInterval(state.tailTimer); state.tailTimer = null; } |
| 307 | if (state.switchWatcher) { clearInterval(state.switchWatcher); state.switchWatcher = null; } |
| 308 | attachTranscript(newer, 0); |
| 309 | } |
| 310 | } catch {} |
| 311 | }, 500); |
| 312 | } |
| 313 | |
| 314 | function startTailing() { |
| 315 | state.tailRemainder = Buffer.alloc(0); |
no test coverage detected