English · 简体中文 · 日本語 · Español · Français · فارسی
Claude Code는 긴 세션을 자동으로 압축합니다. 대화의 대부분을 지우고 요약으로 대체한 뒤 그대로 진행합니다. 보통은 두 시간 전에 정리한 내용을 모델이 다시 묻기 시작할 때 알아차리게 됩니다.
이 저장소는 다른 방식을 택합니다. 세션이 실제로 얼마나 찼는지 측정하고, 끝이 정말 가까워졌을 때 세션 전체를 디스크로 내보낸 뒤 새 세션에서 이어갑니다. 그 새 세션은 무엇이든 시작하기 전에 부모 세션의 기록을 전부 읽습니다. 요약으로 사라지는 것은 없고, 세션의 연결은 항상 보이며 다시 열 수 있습니다.
이 도구를 전혀 설치하지 않더라도 알아둘 만한 설정 함정도 함께 진단합니다. 잘려나가는 창을 보세요.
Python 3.8+와 Claude Code가 필요합니다. 그 외 설치할 의존성은 없습니다.
git clone https://github.com/IRDcode/claude-code-session-handoff
cd claude-code-session-handoff
python install.py --dry-run # 바뀌는 내용을 먼저 확인
python install.py그다음 Claude Code를 재시작하고 무엇이 측정되었는지 확인합니다:
python ~/.claude/skills/long-session-handoff/scripts/session_weight.py --explain기본 설치는 Claude Code의 컨텍스트 관리 방식을 바꾸지 않습니다. 자동 압축은 그대로 두고, 가드가 압축이 발동하기 전에 인계를 마칩니다. 전부 제거하려면:
python install.py --uninstallsettings.json은 수정 전에 백업되고, 기존 hooks와 상태줄은 건드리지 않으며, 두 번
설치해도 아무 일도 일어나지 않습니다.
| 경로 | 내용 |
|---|---|
~/.claude/skills/long-session-handoff/ |
모델이 따르는 절차와 스크립트 3개 |
~/.claude/hooks/session-weight-watch.py |
감지기, 네 개 이벤트에 등록 |
~/.claude/hooks/statusline-weight.py |
매 렌더마다 상태줄에 사용량 표시 |
~/.claude/runtime/ |
반복 알림 억제 상태, 로그, 측정 캐시 |
~/.claude/handoffs/ |
내보낸 파일들과 부모-자식을 잇는 chains.json |
등록되는 hook 이벤트는 네 개입니다 — UserPromptSubmit, SessionStart, PreCompact,
PostCompact. 해당 이벤트에 이미 있던 hooks는 유지됩니다.
| 기본 Claude Code | 이 도구 설치 후 | |
|---|---|---|
| 세션이 찼을 때 | 자동 압축 발동, 대화 대부분이 버려지고 요약으로 대체 | 그보다 훨씬 앞서 인계를 먼저 제안 |
| 다음 세션이 아는 것 | 요약이 담아낸 만큼 — 그런데 그 요약을 쓴 것은 이미 흐름을 놓치던 agent | 부모의 기록 전체를 읽고, 개수로 검증 |
| 버려진 이력 | 현재 세션에서는 참조되지 않음 | 디스크에서 05-dropped-context.md로 복구 |
| 실제로 얼마나 찼나? | /context가 창 기준 비율을 표시 |
상태줄이 세션을 실제로 끝내는 벽 기준 비율을 표시 |
| 나중에 이어진 세션 찾기 | /resume 스크롤 |
chains.json에 부모·자식·이전 시점 사용량·읽기 검증 여부 기록 |
상태줄은 이렇게 보입니다:
Opus 5 | ████████░░ 85% 830k/977k | 696t 402tc 6.1h | HANDOFF DUE (5) | no-compact
벽 기준 비율이며 창 기준이 아닙니다. 둘은 다르고, 때로는 5배까지 차이가 납니다. 그것이 다음 절의 요지입니다.
아무것도 설치하지 않더라도 이 절은 읽어둘 가치가 있습니다.
Claude Code에는 세션이 끝나는 지점이 두 개 있습니다:
압축 발동 창 − 응답 예약(~20k) − 요약 버퍼(~13k)
전송 거부 상한 − 응답 예약(~20k) − 여유(~3k)
자동 압축이 켜져 있으면 앞의 것, 꺼져 있으면 뒤의 것입니다. 즉 200,000 창은 대략 167,000에서 압축됩니다.
함정은 여기입니다. settings.json의 autoCompactWindow는 모델 상한으로 조용히
잘립니다. 상한이 200,000인데 1,000,000을 요청하면 200,000을 얻고, UI 어디에도 그
사실이 표시되지 않습니다. 백만 토큰용으로 설정한 세션이 167,000에서 세 번 연속
압축되는 동안, 이미 비용을 지불한 약 830,000 토큰이 그대로 남습니다.
가정이 아닙니다. 이 저장소가 시작된 지점 그 자체입니다. preTokens가
167,398 / 167,071 / 166,904인 세 번의 압축, 그리고 설정 파일에는
autoCompactWindow: 1000000이 적혀 있었습니다.
--explain이 당신이 어느 쪽인지 알려줍니다:
WINDOW
client reported 1,000,000
ceiling 1,000,000 (source DISABLE_COMPACT+CLAUDE_CODE_MAX_CONTEXT_TOKENS)
resolved 1,000,000 (source settings)
WALL -- the token count past which no more work happens here
1,000,000 ceiling - 20,000 reply reserve - 3,000 margin
= 977,000 then SENDING IS REFUSED (no summary; a handoff is the only exit)
settings CLAMPED to …가 보이면 창 설정이 낮춰지고 있는 것입니다.
잘림을 피하는 구성은 하나뿐입니다 — DISABLE_COMPACT=1과
CLAUDE_CODE_MAX_CONTEXT_TOKENS를 함께 쓰는 것. 설치 프로그램이 설정해 줄 수 있지만,
먼저 묻고 대가를 알려줍니다. 수동 /compact도 함께 비활성화되기 때문입니다:
python install.py --disable-compact --window 1000000이 설정을 하면 세션은 요약으로 끝나지 않고 전송 거부로 끝납니다. 실제 트레이드오프입니다. 거부는 견딜 수 있습니다 — 인계하고 계속하면 됩니다. 조용히 파괴된 이력은 견딜 수 없습니다. 다만 모든 알림을 무시하고 벽까지 가면 그 세션은 새 턴을 받지 않습니다. 그건 미리 알아야 합니다. 가드는 85%에서 발동하며 약 147,000 토큰의 여유를 남기므로, 실제로는 거기까지 가지 않습니다.
/compact를 남겨두고 싶다면 이 플래그는 쓰지 마세요. 인계 기능은 그대로 작동합니다.
여기에는 Claude Code를 패치하거나 감싸는 코드가 없습니다. 문서화된 두 인터페이스 — hook의 stdin/stdout 계약과 상태줄 페이로드 — 를 읽고, 클라이언트가 이미 기록하는 JSONL 전사를 파싱할 뿐입니다. 내부 구현에 의존한 도구라면 깨질 업데이트를 이것이 넘길 수 있는 이유가 거기에 있습니다.
정확한 숫자가 필요한 곳에서는 단정이 아니라 증거를 씁니다. 세 층입니다:
- 창은
context_window_size— 클라이언트가 상태줄을 그릴 때마다 자신에 대해 보고하는 값입니다. - 그 세션이 압축된 적이 있다면, 발동 지점은 전사에 남은 당시의
preTokens에서 가져옵니다. 계산값이 아니라 관찰된 발동 지점 그 자체입니다.score()가 이것을 우선하며, 그럴 때corrected from observed preTokens라고 표시합니다. - 둘 다 없을 때만 예약 산술로 돌아갑니다.
--explain이 모든 입력을 보여주므로, 어긋남은 조용히 진행되지 않고 눈에 보입니다.
구버전 Claude Code. 네 개의 hook 이벤트와 상태줄은 여러 릴리스에 걸쳐 안정적입니다.
당신의 버전에 없는 이벤트가 있으면 그 hook은 발동하지 않고 나머지는 그대로 동작합니다 —
감지기는 대체가 아니라 추가입니다. --disable-compact만 구체적인 설정 이름에 의존하며,
효과가 없었다면 --explain이 알려줍니다.
운영체제. 순수 Python, 의존성 없음, 컴파일된 부분 없음. 경로는 모두 os.path를 거치고,
CLAUDE_CONFIG_DIR은 어디서나 존중되며, 설치 프로그램은 하드코딩하지 않고 당신의
셸에서 실제로 동작하는 인터프리터 이름을 고릅니다. 플랫폼 의존 코드는 stdout을 UTF-8로
고정하는 부분뿐이며, Windows에는 필요하고 다른 곳에서는 무해합니다.
당신의 환경에서 직접 검증하세요:
python tests/test_session_weight.py # 산술, 게이트, 두 가지 함정
python tests/test_compat.py # 문법 하한, 진입점, hook 출력test_compat.py는 이 컴퓨터에 있는 다른 모든 Python을 찾아 각각에서 스위트를 다시
실행합니다. 버전 차이는 나중의 놀라움이 아니라 지금의 실패로 드러납니다.
일곱 개 신호를 측정합니다. 옮길지 여부는 컨텍스트만 결정하고, 나머지는 얼마나 급한지에만 영향을 줍니다.
| 신호 | 임계값 |
|---|---|
| 컨텍스트 vs 벽 | ≥ 85% → 인계, ≥ 95% → 묻지 않고 실행 |
| assistant 턴 수 | ≥ 900 |
| 도구 호출 | ≥ 600 |
| 실작업 시간 | ≥ 4시간 |
| 자동 압축이 이미 발생 | 1회 이상 |
벽의 62% 아래에서는 다른 신호가 어떻든 제안하지 않습니다. 이 게이트가 있는 이유는 나머지 신호가 모두 컨텍스트 압력의 대리 지표 — 컨텍스트를 직접 측정할 수 없던 시절의 발명 — 이기 때문입니다. 이 도구를 만든 세션에서 실측한 결과: 4.3시간의 작업과 두 번의 선행 압축으로 점수는 "지금 인계"에 도달했지만, 컨텍스트는 977,000 중 147,527 — 15% 였습니다. 그때 옮겼다면 829,473 토큰을 아무 대가 없이 버리는 셈이었습니다.
사소해 보이지만 중요한 측정 사항 두 가지:
- 실작업 시간은 10분 미만 간격의 합이며, 마지막에서 처음을 뺀 값이 아닙니다. 밤새 열어둔 세션은 구간 44시간, 작업 11시간으로 나옵니다. 구간으로 점수를 매기면 그냥 방치된 세션에서 인계가 발동합니다.
- 압축 횟수는 기록의 타입이 지정된 행에서 셉니다. 마커 문자열 검색은 절대 쓰지 않습니다. 한 번 검색하면 그 문자열이 자기 도구 출력에 나타나고, 카운트가 스스로 불어납니다.
알림은 단계별로 최대 한 번 — 200턴이 더 지나거나, 벽의 10분의 1을 더 넘을 때 — 그리고 15분의 하한이 있습니다. 서브에이전트 안에서는 절대 발동하지 않습니다.
측정 → 질문 → 내보내기 → 이어질 세션 생성 → 그 세션이 부모를 읽음
내보내기는 다섯 개 파일을 만듭니다. 사용자의 모든 메시지를 원문대로(턴 중간에 보낸 — 잃어버리기 쉬운 — 것들도 포함), 실질적인 assistant 메시지 전부, 도구 출력을 잘라낸 전체 기록, 개수 색인, 그리고 이전 압축이 버린 내용입니다.
그다음 이어질 세션이 만들어지고, 당신이 열기 전에 헤드리스로 깨워져 내보낸 내용을 읽습니다. 색인과 일치하는 개수를 반드시 회신해야 합니다. 일치하지 않으면 읽기가 부분적이었고 인계는 끝나지 않은 것입니다. 이 읽기는 아무도 기다리지 않는 세션에서 일어나므로, 이전 작업의 무거운 부분에 대기 시간이 들지 않습니다.
이후 id와 이름을 받습니다:
claude --resume 7157caa1-11ce-4f29-a46a-09913d483fb0
또는 /resume에서 이름으로 검색합니다. 이름에는 부모의 주제어와 (cont. 2)가 들어
있습니다.
위의 모든 주장은 당신의 환경에서 확인할 수 있습니다. 스크립트는 위로가 아니라 숫자를 출력합니다:
# 내 세션은 어디서 끝나고, 그 이유는 무엇인가
session_weight.py --explain
# 현재 사용량, 모든 신호와 함께
session_weight.py --session-id <uuid>
# 기계가 읽을 수 있는 형태
session_weight.py --session-id <uuid> --json설정 변경이 적용되었는지 확인할 때는 파일을 믿지 말고 기록을 읽으세요. 토큰 합계가 이전 임계값을 넘은 첫 턴을 찾고, 그 뒤에 새 압축 행이 없는지 확인합니다.
측정하는 도구라면 측정하지 않은 것에 대해서도 정직해야 하므로 분명히 적습니다:
- 각 예약값(~20k / ~13k / ~3k)은 관찰된 동작에서 도출했습니다. 향후 릴리스에서 바뀔 수
있습니다. 방어는 세 층입니다: 클라이언트가 직접 보고하는 창, 기록에 남은 실제
preTokens(관찰된 발동 지점이며score()가 이것을 우선합니다), 그리고 마지막으로 이 예약 산술 — 추정인 것은 세 번째 층뿐입니다. - 전송 거부 벽은 계산과 교차 확인으로 얻은 값이며, 의도적으로 도달해 본 것은 아닙니다. 가드는 거기에 이르지 않도록 설계되었습니다.
- Windows에서 Python 3.11, 3.12, 3.14로 테스트했고 3.8 문법에 대해서도 검사했습니다. Linux와 macOS도 동작할 것으로 보이지만 — 콘솔 인코딩 외에 플랫폼 의존 코드는 없습니다 — 둘 다 처음부터 끝까지 실행해 보지는 않았습니다.
- 다섯 개 파일 내보내기와 점수 계산기는 테스트 스위트가 검증합니다. 헤드리스 깨우기는 당신의
claude실행 파일을 띄울 수 있는지에 달려 있습니다. 띄울 수 없더라도 내보내기는 성공하며, 다음에 무엇을 해야 하는지 도구가 알려줍니다. - 프롬프트 캐시: 인계는 새 세션을 시작하므로 캐시가 차가운 상태에서 출발합니다. 벽에 가까운 세션에는 유리한 거래지만, 비용은 비용입니다.
버그 리포트를 환영합니다. 특히 "내 환경에서는 숫자가 다르다" — --explain 출력을 함께
첨부해 주세요. Claude Code 업데이트로 이 산술이 바뀌었다면, 그 리포트가 가장 빠르게
고칠 수 있게 해줍니다.
PR을 열기 전에 두 스위트를 실행해 주세요:
python tests/test_session_weight.py
python tests/test_compat.pySECURITY.md에 무엇을 읽고, 무엇을 쓰고, 네트워크로 무엇을 보내는지 (아무것도 보내지 않습니다) 명시해 두었습니다. 세션 파일을 건드리는 것을 설치하기 전에 한 번 보시길 권합니다.
MIT — LICENSE 참고. 상업적 이용을 포함해 자유롭게 사용·수정·재배포할 수 있습니다. 유일한 조건은 저작권 표시와 라이선스 본문을 함께 유지하는 것이며, 그래서 포크나 재패키징된 사본에도 출처가 남습니다.
이 접근이나 발견 — 특히 창이 잘려나가는 진단 — 을 사용했다면 링크를 남겨주시면
감사하겠습니다. CITATION.cff가 있어 GitHub의 "Cite this repository" 버튼이 올바른
인용 정보를 만들어 줍니다.
작성자: IRDkiya