Claude Code 멀티 세션 관리: csm CLI와 대화형 TUI 만들기

Claude Code로 여러 프로젝트를 동시에 굴리다 보면 터미널 창이 기하급수로 늘어난다. 어떤 창이 무슨 작업이었는지 기억이 안 나고, 우선순위는 뒤섞이며, 완료하지 않은 세션만 계속 쌓인다. 이 글은 그 문제를 해결하려고 직접 만든 claude-session-manager 스킬과 csm CLI의 필요성, 구조, 강점을 정리한 포스트다.

💡 TL;DR

  • 문제: Claude Code 세션이 창 여러 개에 흩어져 있어 찾기·우선순위·정리가 모두 어렵다.
  • 해결: 로컬 JSON 레지스트리 + csm CLI + 대화형 TUI + Claude Code 슬래시 명령 + 상태바를 하나로 묶었다.
  • 강점: iTerm2 / Terminal.app / WezTerm / Kitty / tmux 네이티브 IPC + 제목 매칭 범용 폴백으로 “그 창으로 이동”이 한 줄 명령으로 된다.
  • 부가 기능: 진행 중 작업 캡처, stale 감지, 자동 재개, rich Table 출력, curses TUI.

왜 필요했나

Claude Code를 본격적으로 쓰는 개발자라면 동일한 현상을 겪는다. 프로젝트별로 터미널을 열어 claude를 실행하고, 한참 뒤에 다른 창으로 돌아가려 하면 “어느 창이었지?” 싶다. 창 제목만 보고 구분이 안 되며, 어떤 세션이 중요한 작업이었는지도 모호하다. 완료하지 못한 세션은 그대로 방치돼 며칠 뒤 다시 보면 맥락이 날아간다.

Claude Code 멀티 세션 관리: 혼란에서 통합 관리로
문제: 창마다 무슨 작업인지 알 수 없음 → 해결: 우선순위·상태가 붙은 통합 목록

핵심 사용자 요구는 세 가지였다.

  • 어떤 창이 무슨 작업인지 한눈에 — 제목, 프로젝트 경로, 마지막 프롬프트, 현재 수행 중인 tool_use까지.
  • 우선순위 부여와 정렬 — high/medium/low, stale 자동 감지.
  • 그 창으로 바로 이동 — “ID 선택 → 해당 터미널 창이 맨 앞으로” 한 단계로.

아키텍처 한 장 요약

claude-session-manager 아키텍처: CLI, TUI, Statusline, Slash 4개 표면이 하나의 레지스트리 공유
4개의 UI 표면이 같은 Python 모듈과 파일 기반 레지스트리를 공유한다

네 가지 사용자 접점이 모두 동일한 데이터를 본다.

  • csm CLIlist, set, done, archive, focus, resume, gc 등. TTY일 때 rich Table로 컬러 출력.
  • csm watch — curses 기반 대화형 TUI. 화살표 이동, 검색(/), 인라인 편집, 자동 resume.
  • Statusline — Claude Code 하단 상태바에 📋 3 pending · 1 stale 실시간 표시.
  • Slash commands/tasks, /done, /task-register, /task-priority, /task-focus 등 8개.

데이터 레이어는 ~/.claude/claude-tasks/<session-uuid>.json 파일 한 세션당 하나로 단순화했다. 원자적 쓰기(tempfile + os.replace), 손상 파일 자동 격리, 마이크로초 정밀도 타임스탬프로 다중 프로세스 안전성을 확보했다.

Claude Code의 SessionStart/UserPromptSubmit 훅이 각 세션 시작과 프롬프트마다 레지스트리를 자동 갱신한다. 훅은 stdout을 클로드가 가로채므로 창 제목 마커는 /dev/tty로 직접 기록한다.

플래그십: “그 창으로 이동”

이 프로젝트에서 가장 공들인 부분이다. 터미널마다 IPC 방식이 전혀 다르고, 어떤 것은 아예 IPC가 없다. 그래서 4단계 전략을 구성했다.

csm focus 4단계 전략: tmux → 네이티브 IPC → 제목 매칭 → resume 폴백
csm focus는 4단계 전략으로 어떤 터미널에서도 창을 찾는다
  1. tmux 우선 — 세션이 tmux 안에 있으면 tmux select-pane + switch-client로 먼저 판을 전환.
  2. 네이티브 IPC — iTerm2/Terminal.app(AppleScript), WezTerm(wezterm cli activate-pane), Kitty(kitty @ focus-window).
  3. 제목 매칭 범용 폴백 — SessionStart 훅이 창 제목에 csm:<short-id>를 OSC-0 escape로 박아둔다. macOS System Events / X11 wmctrl·xdotool / Wayland swaymsg로 일치하는 창을 찾는다. Ghostty, Alacritty, Warp 같이 IPC 없는 터미널도 이 경로로 동작한다.
  4. resume 제안 — 위 셋 모두 실패 시 csm resume <id>로 새 창에서 claude --resume 실행을 안내. 세션 자체는 Claude Code 트랜스크립트에 남아 있으므로 맥락 손실 없이 이어갈 수 있다.

지원 터미널 매트릭스

기능 iTerm2 Terminal.app WezTerm Kitty Ghostty / Alacritty / Warp tmux
focus ✅ AppleScript ✅ AppleScript ✅ wezterm cli ✅ kitty @ ✅ 제목 매칭 ✅ select-pane
resume ✅ AppleScript ✅ AppleScript ✅ wezterm cli spawn ✅ kitty @ launch ⚠️ 앱만 열고 안내
watch –pin
창 열림 감지 ✅ 제목 스캔 ✅ 제목 스캔 ✅ cli list ✅ 제목 스캔 ✅ 제목 스캔
터미널별 지원 범위. 제목 매칭 덕분에 IPC가 없는 터미널도 focus가 동작한다.

진행 중 작업 캡처

창을 찾기만 해서는 부족하다. 그 창에서 지금 뭘 하고 있었는지도 알아야 한다. 스캐너가 각 세션의 JSONL 트랜스크립트를 읽어 세 필드를 추출한다.

  • last_user_prompt — 마지막 사용자 메시지 (100자 절단, 유니코드 경계 보존).
  • last_assistant_summary — Claude 최근 응답의 첫 줄.
  • current_task_hint — 마지막 tool_use에서 파생 (예: Running: pytest -q tests/, Editing: scripts/cst.py).

스캐너 덮어쓰기와 훅 쓰기가 충돌할 수 있어 “fresher wins” 규칙을 넣었다. JSONL mtime과 저장된 last_activity_at을 마이크로초 정밀도로 비교해, 정말 새로운 데이터일 때만 스캐너가 덮는다. 훅이 방금 쓴 프롬프트를 스캐너가 낡은 JSONL로 되돌리는 사고를 막는다.

대화형 TUI

csm watch는 curses 기반 TUI다. claude-sessions 스킬의 픽커 UX에 착안했다.

키 바인딩

  • ↑↓ / k / j 이동, PgUp/PgDn/Home/End 페이지 이동
  • Enter focus (창 닫혀 있고 in_progress면 자동 resume)
  • r resume, n 메모 편집, p 우선순위 순환, s 상태 순환
  • d done, a archive, / 검색, ? 도움말, q/Esc 종료

선택된 행의 프로젝트 경로는 컬럼 폭을 넘을 때 마키(carousel) 스크롤로 흐른다. 한글/이모지 너비를 측정해 정렬이 깨지지 않도록 했다. 도트 표시로 상태를 구분한다.

  • claude 프로세스가 tty에 살아 있음
  • 창은 열려 있으나 claude 종료됨
  • 창이 닫혔거나 구 세션

왜 직접 만들었나

기존 도구 조합으로 어느 정도 흉내는 낼 수 있었지만, 하나의 응집된 워크플로우로 묶기에는 부족했다.

접근 한계
터미널 Tab 제목 바꾸기 우선순위·상태 추적 없음, 여러 창 비교 불가.
tmux 세션 이름 tmux 쓰는 경우 한정, Claude 세션 맥락과 분리됨.
에디터 TODO / Obsidian 수동 입력 필요, 창 포커싱 불가.
claude-session-manager 자동 감지 + 우선순위 + 포커싱 + 재개 + 상태바까지 한 도구로 통합.

내부 품질

Anthropic의 Harness Design 패턴(Planner → Generator → Evaluator 3-agent 루프)을 적용해 3번의 Sprint로 개발했다. 각 Sprint마다 계약서(sprint_contract.md)를 먼저 작성하고, Evaluator가 독립 컨텍스트에서 실제 명령을 돌려 검증했다. 결과물은 pytest 186개 + 관찰 체크 32개 전부 통과한 상태로 완성됐다.

  • Python 3.11+ stdlib 기반 (TUI는 curses, 표는 rich만 선택적 의존)
  • 원자적 파일 쓰기, 손상 JSON 자동 격리, 설정 파일 검증
  • macOS, Linux(X11/Wayland), tmux 환경 모두에서 focus 동작
  • 훅 실패가 절대 사용자 세션을 블로킹하지 않음 (exit 0 보장)

저장소

GitHub: github.com/greeun/claude-session-manager

MIT 라이선스. 영어 / 한글 README 포함. pytest 186개 통과. 이슈와 PR 환영.

설치와 사용

git clone <repo> claude-session-manager
cd claude-session-manager
bash install.sh
export PATH="$HOME/.local/bin:$PATH"

csm list          # 세션 목록 (자동 스캔)
csm watch         # 대화형 TUI
csm focus abc123  # 해당 창 앞으로
csm resume abc123 # 새 창에서 claude --resume

Claude Code 안에서는 /done, /task-register "로그인 API" high, /task-focus 2 식으로 쓴다. 상태바에는 자동으로 📋 3 pending · 1 stale이 붙는다.

마치며

AI 어시스턴트를 창 하나로만 쓰던 시절은 지났다. 여러 세션을 동시에 굴리면서 “지금 뭐 하던 거였지”를 줄이는 것만으로도 작업 효율이 체감될 만큼 올라간다. Claude Code는 트랜스크립트를 계속 남기니, 창이 닫혀도 세션은 사라지지 않는다. 그 자산을 제대로 활용하려면 지금 내가 어떤 세션들을 갖고 있는지 한눈에 보여주는 도구가 필요하다.

현재 v0.3.0까지 공개돼 있다. 다음은 macOS Notification Center 연동, Linear 이슈 자동 동기화, 그리고 다중 머신 레지스트리 병합을 고민하고 있다.

댓글 남기기