feat: rewrite detection with sessions/ PID files + cross-ref fallback
Merged
GR
7mo ago+319+319+012345678901234567890123456789-113-113-012345678901234567890123456789
Summary
Rewrite active session detection to use ~/.claude/sessions/<PID>.json as primary source, with cross-reference fallback for rare same-cwd ambiguity.
Pros
| Improvement | Before | After |
|---|---|---|
| Detection function speed | ~200-450ms (ps aux + lsof + cross-ref) | ~5ms (fs read only, no process spawn) |
| End-to-end purple dot | ~0.2-0.3s visible delay | ~0.1s (user-observed) |
| Same-cwd detection (no /rename) | May assign wrong PID to session | ✓ Direct PID→sessionId from sessions/ |
| VS Code sessions | Not detected | ✓ (entrypoint: "claude-vscode") |
| Claude Desktop sessions | Not detected | ✓ (entrypoint: "claude-desktop") |
-n session name | Parse process args | Direct name field |
/clear handling | Wrong sessionId | cwd fallback (correct for single same-cwd) |
| Cross-ref frequency | All cwd-fallback PIDs | Only sessionId-mismatch + same-cwd (rare) |
| Process spawning | ps aux + N × lsof + pgrep + AppleScript/CLI | fs.readdir + fs.readFile (no spawn for most cases) |
Cons / Risks
| Risk | Mitigation |
|---|---|
sessions/ is undocumented Claude Code internal | Other tools already use it (agent-sessions, c9watch). Fallback to legacy if dir doesn't exist |
sessionId mismatch after /clear | cwd fallback + cross-ref disambiguation |
| Unknown mismatch cause (observed once) | Same cwd fallback handles it |
| Same session resumed twice → one not marked | Edge case, noted in #65 |
sessions/ could be removed in future Claude Code | Legacy fallback preserved |
| Both detection layers fail (#68) | Requires /clear + same-cwd + no /rename + Ghostty — very low likelihood |
Detection flow
1. Read sessions/*.json → PID + sessionId + cwd + entrypoint (~5ms)
2. sessionId match history.jsonl → perfect (most cases)
3. No match + 1 same-cwd → cwd match (safe)
4. No match + multiple same-cwd → cross-ref by terminal:
Promise.all([
iTerm2: AppleScript TTY+name → match custom titles (~130ms)
cmux: tree --all tty= → match custom titles (~70ms)
])
Then: Ghostty/other: cwd fallback (~0ms)
5. sessions/ not found → legacy (ps aux + regex + lsof)
Test results
All launch methods tested — sessionId matches history.jsonl in all cases except /clear:
| Command | Match? |
|---|---|
claude (bare) | ✓ |
claude -n "name" | ✓ |
claude --resume <uuid> | ✓ |
claude -r <uuid> | ✓ |
claude --resume "title" | ✓ |
claude -r "title" | ✓ |
claude -r (picker) | ✓ |
claude -c | ✓ |
/rename | ✓ (name updated, sessionId unchanged) |
/clear | ✗ (temporary — resolves after exit+resume) |
Same-cwd detection (two bare claude, no /rename):
- iTerm2: both purple dots correct ✓, switch correct ✓ (TTY match)
- Ghostty: both purple dots correct ✓, switch may be wrong (cwd fallback, no TTY)
- cmux: both purple dots correct ✓, switch → see PR for cmux TTY switch layer
Fixes #65
🤖 On behalf of @grimmerk — generated with Claude Code