(cwd, limit = 20)
| 407 | // Session Scanner |
| 408 | // ============================================================ |
| 409 | function scanSessions(cwd, limit = 20) { |
| 410 | const dir = path.join(PROJECTS_DIR, getProjectSlug(cwd)); |
| 411 | let files; |
| 412 | try { |
| 413 | files = fs.readdirSync(dir).filter(f => f.endsWith('.jsonl')); |
| 414 | } catch { |
| 415 | return []; |
| 416 | } |
| 417 | |
| 418 | const entries = []; |
| 419 | for (const f of files) { |
| 420 | const full = path.join(dir, f); |
| 421 | try { |
| 422 | const stat = fs.statSync(full); |
| 423 | entries.push({ file: f, full, mtime: stat.mtimeMs, size: stat.size }); |
| 424 | } catch {} |
| 425 | } |
| 426 | entries.sort((a, b) => b.mtime - a.mtime); |
| 427 | const top = entries.slice(0, limit); |
| 428 | |
| 429 | const sessions = []; |
| 430 | for (const entry of top) { |
| 431 | const sessionId = path.basename(entry.file, '.jsonl'); |
| 432 | const info = { |
| 433 | sessionId, |
| 434 | summary: '', |
| 435 | firstPrompt: '', |
| 436 | lastModified: Math.round(entry.mtime), |
| 437 | fileSize: entry.size, |
| 438 | cwd: cwd, |
| 439 | }; |
| 440 | |
| 441 | try { |
| 442 | const fd = fs.openSync(entry.full, 'r'); |
| 443 | const buf = Buffer.alloc(Math.min(entry.size, 64 * 1024)); |
| 444 | fs.readSync(fd, buf, 0, buf.length, 0); |
| 445 | fs.closeSync(fd); |
| 446 | const lines = buf.toString('utf8').split('\n').filter(Boolean); |
| 447 | for (const line of lines) { |
| 448 | try { |
| 449 | const evt = JSON.parse(line); |
| 450 | if (!info.firstPrompt) { |
| 451 | info.firstPrompt = extractSessionPrompt(evt); |
| 452 | } |
| 453 | if (!info.model && evt.model) { |
| 454 | info.model = evt.model; |
| 455 | } |
| 456 | } catch {} |
| 457 | } |
| 458 | } catch {} |
| 459 | |
| 460 | info.summary = info.firstPrompt || 'Untitled'; |
| 461 | sessions.push(info); |
| 462 | } |
| 463 | return sessions; |
| 464 | } |
| 465 | |
| 466 | // ============================================================ |
no test coverage detected