feat: rewrite detection with sessions/ PID files + cross-ref fallback

Merged
GR
7mo ago
+319-113

Summary

Rewrite active session detection to use ~/.claude/sessions/<PID>.json as primary source, with cross-reference fallback for rare same-cwd ambiguity.

Pros

ImprovementBeforeAfter
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 sessionsNot detected✓ (entrypoint: "claude-vscode")
Claude Desktop sessionsNot detected✓ (entrypoint: "claude-desktop")
-n session nameParse process argsDirect name field
/clear handlingWrong sessionIdcwd fallback (correct for single same-cwd)
Cross-ref frequencyAll cwd-fallback PIDsOnly sessionId-mismatch + same-cwd (rare)
Process spawningps aux + N × lsof + pgrep + AppleScript/CLIfs.readdir + fs.readFile (no spawn for most cases)

Cons / Risks

RiskMitigation
sessions/ is undocumented Claude Code internalOther tools already use it (agent-sessions, c9watch). Fallback to legacy if dir doesn't exist
sessionId mismatch after /clearcwd fallback + cross-ref disambiguation
Unknown mismatch cause (observed once)Same cwd fallback handles it
Same session resumed twice → one not markedEdge case, noted in #65
sessions/ could be removed in future Claude CodeLegacy 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:

CommandMatch?
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