tmux-assistant-resurrect
면책 조항 (Disclaimer): 이 프로젝트는 전적으로 'vibecoded' 방식으로 제작되었습니다 (AI 코딩 어시스턴트와의 대화를 통해 설계 및 구현됨). Docker 환경에서 실제 CLI 바이너리(Claude/Copilot/OpenCode/Codex/Pi/Oh My Pi)를 사용하여 엔드투엔드 테스트를 완료했습니다 (400개 이상의 자동화 테스트 + 전체 저장/종료/복구 라이프사이클 스모크 테스트). Cursor와 Grok은 격리 단위 테스트(hermetic unit tests)가 지원되지만, 인증된 Docker 통합 테스트는 없습니다.
실제 사용 사례가 제한적이므로, 다소 미흡한 부분이 있을 수 있습니다. 기여 및 버그 보고를 환영합니다.
tmux 재시작 및 재부팅 시에도 AI 코딩 어시스턴트 세션을 유지하고 복구합니다.
Save, kill, and restore — assistant sessions resume automatically
컴퓨터가 종료되면, 실행 중이던 모든 것(예: Claude Code, Cursor Agent CLI, GitHub Copilot CLI, OpenCode, Codex CLI, Pi, Oh My Pi, Antigravity CLI (agy), 또는 Grok Build (xAI의 grok CLI / TUI, 커뮤니티 grok-cli 아님) 인스턴스 등)가 손실됩니다. 이 프로젝트는 tmux-resurrect에 연결되어 어시스턴트 세션 ID, CLI 플래그 및 환경 변수를 자동으로 저장하고, 복구 후 정확히 동일한 구성으로 다시 실행합니다.
작동 방식 (How it works)
SAVE (5분마다 + 수동 prefix+Ctrl-s)
tmux-resurrect가 패널 레이아웃을 저장함
-> post-save hook이 각 패널의 자식 프로세스를 검사함
...
설계 (Design)
감지(Detection)는 직접적인 프로세스 검사를 통해 이루어집니다: 저장 스크립트는 모든 프로세스의 단일 ps 스냅샷을 가져와, 각 tmux 패널 쉘의 자식 프로세스를 찾고, 알려진 어시스턴트 바이너리 이름(claude, agent/cursor-agent, copilot, opencode, codex, pi, omp, grok, agy)과 일치하는지 확인합니다.
세션 ID 추출은 도구 고유 메커니즘(인프라 배관)을 사용합니다:
| Tool | Primary method | Fallback 1 | Fallback 2 | Notes |
|---|
| Claude Code | SessionStart hook state file (keyed by Claude PID) | --resume / --session-id in process args | Newest non-empty cwd-scoped transcript under ~/.claude/projects/ | 트랜스크립트 조회는 PID별 상태/argv에 의해 예약된 ID와 다른 패널에서 이미 방출된 ID를 제외하지만, 하나의 cwd 내에 두 개의 해결되지 않은 Claude 인스턴스가 남아 있는 경우 모호하며 포기된 세션을 선택할 수 있습니다 |
| ... | | | | |
| 각 도구는 기본 추출 방법과 대체(fallback) 추출 방법을 가집니다. 대체 방법은 '닭이 먼저냐 달걀이 먼저냐' 문제(chicken-and-egg problem)를 해결합니다: 복원 후, 훅(hooks)/플러그인이 실행되기 전에도 세션 ID가 프로세스 인자(process args)에 존재하기 때문입니다. OpenCode SQLite 데이터베이스 대체 방법은 플러그인이 실행되지 않았을 때에도 버전 복원력이 있는 세션 ID 추출을 제공합니다. | | | | |
Antigravity CLI (agy)
Antigravity CLI는 agy --conversation <UUID>로 재개됩니다. Save는 소유 PID가 연 로그 파일(Linux의 경우 /proc/<pid>/fd, macOS의 경우 lsof)을 읽고, 명시적인 --log-file을 포함하며, 대화 선택 이벤트를 추적합니다. TUI 내 스위치는 원래의 --conversation 인자를 재정의합니다. 새롭고 빈 대화를 열면 이전 선택이 지워집니다. 선택 이벤트가 존재하지 않으면, 오직 실행 인자에 명시된 UUID만 사용됩니다. cwd/최신 세션 대체 방법이 없으므로, 해결되지 않았거나 빈 대화는 건너뜁니다.
이는 공개된 hook API가 아닌, 관찰되는 CLI 로그 메시지(‘Streaming conversation’, ‘Starting conversation update stream’, 그리고 전체 다시 그리기 이벤트)에 의존합니다. agy의 로깅 방식이 변경될 경우 업데이트가 필요할 수 있습니다. Model, effort 및 permission 옵션은 유지되지만, 초기 프롬프트와 이전 프로세스 로그 경로는 재생되지 않습니다. 상대 경로 로그는 소유 프로세스의 cwd를 기준으로 해결됩니다. Linux는 정확한 argv 경계를 사용하며 공백을 포함하는 값 전체 옵션을 생략하는데, 이는 replay sidecar가 표현할 수 없습니다. 다른 플랫폼은 평탄화된(flattened) argv를 사용하며, 폐기된 로그 경로는 완전히 소비되어 그 꼬리 부분이 --sandbox와 같은 후속 옵션을 잘라내지 않도록 합니다. 실행 래퍼(launch wrapper)나 새로운 hook은 설치되지 않습니다.
테스트는 세 가지 계층으로 구성됩니다:
bash test/agy-unit-tests.sh: 감지, 선택, 인자 경계 및 인증된 테스트의 준비 상태 확인을 포함하며, 실제 /proc/lsof 프로브를 사용합니다.
python3 test/agy-e2e.py: 두 개의 창(pane)이 cwd를 공유하는 실제 agy 프로세스, tmux 재시작에 걸친 프로덕션 저장/복원 기능, 그리고 반복 복원 가드(repeated-restore guards)를 테스트합니다. test/install-agy.sh에서 SHA256으로 고정된 agy 1.4.3 버전으로 CI 환경에서 실행됩니다. 로그인이나 프롬프트가 필요하지 않습니다. 대화 선택 로그 이벤트는 시딩된 피처(seeded fixtures)이며, 이는 인증된 대화 복구가 아닌 라우팅을 검증합니다.
python3 test/agy-live-lifecycle.py /path/to/tmux-resurrect <UUID> 'known old message': 비공개 tmux 소켓에서 옵트인(opt-in) 방식으로 인증된 복구를 수행합니다. 현재 계정/스토어를 사용하며 프롬프트를 보내지 않습니다. 이는 자신의 빈 임시 디렉터리에 대해서만 작업 공간 신뢰를 확인합니다. 성공하려면 요청된 UUID에 대한 실제 다시 그리기(actual redraw)가 이루어져야 하며, 재시작 전후로 제공된 기존 메시지가 창에 보여야 합니다. launch argv만으로는 이 검증을 통과할 수 없습니다.
전제 조건
- tmux (버전 3.4부터 3.7까지 테스트됨)
- TPM (Tmux 플러그인 매니저)
- jq (save/restore 스크립트에서 사용됨)
- 다음 중 하나 이상:
Claude Code, Cursor Agent CLI, GitHub Copilot CLI, OpenCode,
Codex CLI, Pi, Oh My Pi, Grok
설치
TPM이 없다면 설치합니다:
git clone https://github.com/tmux-plugins/tpm ~/.tmux/plugins/tpm
~/.tmux.conf에 추가합니다:
set -g @plugin 'tmux-plugins/tpm'
set -g @plugin 'tmux-plugins/tmux-resurrect'
set -g @plugin 'tmux-plugins/tmux-continuum'
...
그런 다음 tmux 내부에서 prefix + I (대문자 I)를 누릅니다. TPM이 플러그인을 클론하고 자동으로 다음을 설정합니다:
- tmux-resurrect + tmux-continuum 설정
~/.claude/settings.json에 Claude Code 훅(hooks)
~/.cursor/hooks.json에 Cursor Agent CLI 훅(hooks)
- 세션별
inuse.<pid>.lock 파일을 통한 Copilot 지원 (훅 또는 플러그인 필요 없음)
~/.config/opencode/plugins/의 OpenCode v1 세션 추적기 플러그인
~/.pi/agent/sessions에서의 세션 파일 조회를 통한 Pi 지원 (훅 또는 플러그인 필요 없음)
$XDG_STATE_HOME/omp, $XDG_DATA_HOME/omp, 또는 ~/.omp의 터미널/세션 파일 조회를 통한 Oh My Pi 지원 (훅 또는 플러그인 필요 없음)
~/.grok/active_sessions.json 레지스트리를 통한 Grok 지원 (훅 또는 플러그인 필요 없음)
네이티브 훅(hook)/플러그인 설치기는 기본적으로 활성화되어 있습니다. 개별 설치기를 비활성화하려면, ~/.tmux.conf에서 TPM의 run 라인 앞에 해당 옵션을 배치합니다:
set -g @assistant-resurrect-claude 'off'
set -g @assistant-resurrect-cursor 'off'
set -g @assistant-resurrect-opencode 'off'
비활성화하려는 어시스턴트 옵션만 설정합니다. 설정을 적용하려면 tmux 구성을 다시 로드해야 합니다. Claude/Cursor의 제외(opt-out) 기능은 관련 없는 후크, 설정, dotfile 심볼릭 링크 및 대상 모드는 보존하면서 현재 및 오래된 후크 명령어를 제거합니다. OpenCode 설치기는 자체 session-tracker.js 심볼릭 링크(오래된 체크아웃 경로 포함)를 제거하고 일반 파일과 관련 없는 심볼릭 링크는 그대로 둡니다. 'on'으로 설정하거나 옵션을 비워두면, tmux의 PATH에서 호환되는 OpenCode v1 명령어가 발견될 때 설치가 활성화됩니다.
이러한 옵션들은 네이티브 후크/플러그인 설치를 제어하며, 저장/복원 후크나 네이티브 상태/프로세스 인자/데이터베이스 폴백(fallback)을 비활성화하지는 않습니다. 후크가 없는 어시스턴트는 제외할 설치기가 없습니다.
OpenCode v2는 다른 플러그인 API와 별도의 서버 프로세스를 사용합니다. 이 설치기는 연결하기 전에 opencode --version (그리고 존재할 경우 opencode2)를 확인하고, 이전의 v1 링크를 v2, 향후 메이저 버전, 알 수 없는/개발 버전, 실패한 프로브 또는 누락된 바이너리용으로 제거합니다. 이러한 경우에는 OpenCode 구성 디렉토리를 생성하지 않습니다. 전역 플러그인 디렉토리를 공유하는 혼합된 v1/v2 설치의 경우, v2 명령어가 tmux의 PATH에 표시되면 v1 후크도 건너뜁니다.
네이티브 v2 세션 추적은 아직 지원되지 않습니다. 기존의 저장/복원 폴백 기능은 선택된 v2 TUI 세션을 복구한다는 보장이 아닌 최선의 노력입니다. v1을 설치하거나 tmux의 PATH를 수정했다면, 트래커를 설치하기 위해 구성을 다시 로드하십시오. 이전에 로드된 플러그인을 제거한 후에는 OpenCode를 재시작하여 진단 정보를 지우십시오.
제거 (Uninstallation)
TPM 사용자: ~/.tmux.conf에서 @plugin 'timvw/tmux-assistant-resurrect' 라인을 제거한 다음, tmux 내부에서 prefix + alt + u를 누르세요.
just install 사용자: 플러그인 디렉토리에서 just uninstall을 실행하면 Claude 및 Cursor 후크, OpenCode 플러그인 심볼릭 링크, 그리고 ~/.tmux.conf에 관리되는 블록이 제거됩니다.
사용법 (Usage)
자동 (권장)
설치한 후에는 모든 것이 자동으로 실행됩니다.
- tmux-continuum: 5분마다 tmux 레이아웃을 저장합니다.
- 저장 후 훅(Post-save hook): 각 저장 시점의 어시스턴트 세션 ID를 수집합니다.
- tmux 서버 시작 시: continuum이 자동으로 레이아웃을 복원합니다.
- 복원 후 훅(Post-restore hook): 저장된 세션 ID를 사용하여 각 어시스턴트를 재개합니다.
플러그인은 @continuum-save-interval의 기본값을 5로, @continuum-restore의 기본값을 on으로 설정하지만, 이 값들은 이미 설정되어 있는 경우에는 적용되지 않습니다. 사용자가 ~/.tmux.conf에 정의한 값은 절대 덮어쓰기 되지 않습니다.
수동 저장/복원 키 바인딩 (tmux-resurrect 기본값):
| Key | 동작 |
|---|
prefix + Ctrl-s | tmux 상태 + 어시스턴트 세션 저장 |
prefix + Ctrl-r | tmux 상태 + 어시스턴트 재개 |
저장소 구조 (Repository structure)
tmux-assistant-resurrect.tmux # TPM 플러그인 진입점
config/
resurrect-assistants.conf # tmux 설정 참조 템플릿 (자동으로 로드되지 않음)
...
테스트 (Testing)
자동화 테스트 (Docker)
전체 테스트 스위트(test suite)는 실제 CLI 바이너리(mock 없음)를 사용하여 Docker에서 실행됩니다:
빠르고 격리된(hermetic) 스위트는 강화된 저장, 복원 및 설치 경로를 다룹니다:
just test-save-hardening
just test-restore
just test-cursor
...
macOS CI 작업은 또한 고정된 v1 및 v2 바이너리가 별도의 임시 npm 접두사(npm prefixes)에 설치된 실제 OpenCode 플러그인 계약을 실행합니다. 로컬에서 반복하려면 다음 명령어를 사용하세요:
python3 test/opencode-plugin-contract-test.py /path/to/v1/opencode /path/to/v2/opencode
이 명령어는 사설 홈과 서버를 생성하고, v1 세션 이벤트를 검증하며, v2 플러그인 거부(rejection)를 재현하고, 설치 프로그램이 이를 제거하는지(혼합된 opencode v1 / opencode2 v2 설치 포함) 확인합니다. 로그인이나 모델 요청은 필요하지 않습니다.
just test는 tmux, jq, just와 실제 @anthropic-ai/claude-code, @openai/codex, @earendil-works/pi-coding-agent npm 패키지를 사용하여 Docker 이미지를 빌드하고, 설치(install), 저장(save), 복원(restore), 제거(uninstall), 훅(hooks), 정리(cleanup), TPM 플러그인 설치, 세션 ID 추출, POSIX 따옴표 처리(quoting), 프로세스 트리 감지(process tree detection), 업그레이드 경로 마이그레이션(upgrade-path migration), 그리고 회귀 시나리오(regression scenarios)를 포괄하는 전체 테스트 스위트를 실행합니다. Pi는 --offline 플래그와 함께 실행되어 (API 키 없이 TUI로 유지됨) 살아있습니다. API 키가 필요하지 않습니다—이 테스트들은 AI 기능 자체가 아니라 프로세스 감지 및 세션 관리 레이어를 검증합니다.
성능 벤치마크 (Docker)
벤치마크 매트릭스를 실행하고 결과를 CSV + Markdown 형식으로 캡처하세요:
현재 체크아웃을 다른 리포지토리 경로(예: main의 worktree)와 비교하려면:
just benchmark base_repo=/path/to/base/worktree
결과는 다음 위치에 작성됩니다:
test-results/benchmark.csv
test-results/benchmark.md
GitHub Actions(.github/workflows/test.yml)에서는 매 푸시(push)/PR마다 벤치마크 매트릭스를 실행하고, 단계별 요약 테이블을 게시하며, 동일한 CSV/Markdown 파일을 benchmark-results 아티팩트로 업로드합니다.
직접 사용해 보기
일반적인 TPM 설치를 사용하여 본인 기기에서 전체 저장 → 종료 → 복원 사이클을 검증할 수 있습니다. 클로닝이나 빌드 도구가 필요하지 않습니다.
사전 요구 사항: tmux, jq, 그리고 claude / copilot / opencode / codex / pi 중 최소 하나가 설치되어 있어야 합니다.
1. 설치
위의 설치 단계를 따르세요 (TPM 설치 후, ~/.tmux.conf에 플러그인 라인을 추가하고 tmux 내부에서 prefix + I를 누릅니다).
2. 어시스턴트 실행하기
평소처럼 별도의 tmux 창이나 세션에서 어시스턴트를 시작하세요:
cd ~/src/my-project
claude
...