세션 상태
purplemux가 Claude Code의 활동을 어떻게 4가지 상태로 변환하는지, 그리고 어떻게 거의 실시간으로 동기화되는지.
사이드바의 모든 세션에는 색상 점이 붙어 있어, Claude가 무엇을 하고 있는지 한눈에 알려줍니다. 이 페이지는 4가지 상태가 어디서 오는지, 그리고 터미널을 들여다보지 않아도 어떻게 동기화가 유지되는지 설명합니다.
4가지 상태
| 상태 | 인디케이터 | 의미 |
|---|---|---|
| Idle | 없음 / 회색 | Claude가 다음 입력을 기다리는 중 |
| Busy | 퍼플 스피너 | Claude가 작업 중 — 파일 읽기, 편집, 툴 실행 |
| Needs input | 호박색 펄스 | 권한 프롬프트나 질문이 사용자를 기다리는 중 |
| Review | 퍼플 펄스 | Claude가 작업을 마쳤고 확인할 것이 있음 |
5번째 값인 unknown은 서버 재시작 시점에 busy 상태였던 탭에서 잠시 나타납니다. purplemux가 세션을 다시 검증하면 자동으로 해소됩니다.
hook이 single source of truth
purplemux는 서버 시작 시 ~/.purplemux/hooks.json에 Claude Code hook 설정을, ~/.purplemux/status-hook.sh에 작은 쉘 스크립트를 생성합니다. 이 스크립트는 5개의 Claude Code hook 이벤트에 등록되어, 각 이벤트가 발생할 때마다 CLI 토큰과 함께 로컬 서버로 POST합니다:
| Claude Code hook | 결과 상태 |
|---|---|
SessionStart |
idle |
UserPromptSubmit |
busy |
Notification (권한만) |
needs-input |
Stop / StopFailure |
review |
PreCompact / PostCompact |
compacting 인디케이터 표시 (상태는 그대로) |
hook은 Claude Code가 전환되는 순간에 fire되므로, 사이드바는 터미널에서 알아채기 전에 먼저 갱신됩니다.
프로세스 감지는 병렬로
Claude CLI가 실제로 실행 중인지 여부는 작업 상태와는 별도로 추적됩니다. 두 경로가 협력합니다:
- tmux 타이틀 변경 — 모든 pane은
pane_current_command|pane_current_path형태로 자기 타이틀을 보고합니다. xterm.js의onTitleChange로 변경이 전달되면 purplemux가/api/check-claude로 확인합니다. - 프로세스 트리 탐색 — 서버 측에서
detectActiveSession이 pane의 쉘 PID를 보고 자식들을 따라가며,~/.claude/sessions/의 PID 파일과 매칭합니다.
해당 디렉토리가 없으면 상태 점 대신 "Claude not installed" 화면이 표시됩니다.
JSONL watcher가 빈 곳을 채움
Claude Code는 ~/.claude/projects/ 아래에 세션별 트랜스크립트 JSONL을 기록합니다. 탭이 busy, needs-input, unknown, ready-for-review인 동안 purplemux는 두 가지 이유로 fs.watch를 걸어둡니다:
- 메타데이터 — 현재 툴, 마지막 어시스턴트 스니펫, 토큰 카운트. 상태 자체는 바꾸지 않고 타임라인과 사이드바에 흘려줍니다.
- 합성 interrupt — 사용자가 스트리밍 중간에 Esc를 눌렀을 때, Claude는 JSONL에
[Request interrupted by user]를 기록하지만 hook을 fire하지 않습니다. watcher가 이 줄을 감지해interrupt이벤트를 합성하므로, 탭이 busy에 갇히지 않고 idle로 복귀합니다.
polling은 엔진이 아니라 안전망
탭 수에 따라 30~60초마다 메타데이터 polling이 돌지만, 상태 결정에는 관여하지 않습니다. 상태는 엄격하게 hook 경로에서만 결정됩니다. polling의 역할은:
- 새 tmux pane 발견
- busy가 10분 이상 지속되고 Claude 프로세스가 죽은 경우 복구
- 프로세스 정보, 포트, 타이틀 갱신
랜딩 페이지의 "5–15s fallback polling"이 바로 이것이며, hook이 안정화된 이후 간격을 늘리고 범위를 좁힌 결과입니다.
서버 재시작에서 살아남기
서버가 다운된 동안에는 hook이 fire될 수 없으므로, 진행 중이던 상태는 stale해질 수 있습니다. 복구 규칙은 보수적입니다:
- 영속화된
busy는unknown으로 변환된 뒤 재검증됩니다: Claude가 더 이상 실행 중이 아니면 조용히 idle로, JSONL이 깔끔하게 끝나 있으면 review로 전환됩니다. idle,needs-input,ready-for-review는 모두 사용자 차례이므로 그대로 유지됩니다.
복구 과정의 자동 전환은 푸시 알림을 보내지 않습니다. 새로 needs-input이나 review로 진입하는 작업에 대해서만 알림이 옵니다.
상태가 표시되는 곳
- 사이드바 세션 행의 점
- 각 pane의 탭바 점
- 워크스페이스 점 (워크스페이스 내 최우선 상태)
- 벨 아이콘의 카운트와 알림 시트
- 브라우저 탭 타이틀 (attention 항목 카운트)
needs-input과ready-for-review에 대한 Web Push 및 데스크탑 알림