agent-session-manager 0.6.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. agent_session_manager-0.6.0/.claude/settings.local.json +39 -0
  2. agent_session_manager-0.6.0/.gitignore +13 -0
  3. agent_session_manager-0.6.0/.python-version +1 -0
  4. agent_session_manager-0.6.0/LICENSE +21 -0
  5. agent_session_manager-0.6.0/PKG-INFO +209 -0
  6. agent_session_manager-0.6.0/PLANNING.md +360 -0
  7. agent_session_manager-0.6.0/README.ko.md +177 -0
  8. agent_session_manager-0.6.0/README.md +178 -0
  9. agent_session_manager-0.6.0/demo/main.tape +45 -0
  10. agent_session_manager-0.6.0/pyproject.toml +55 -0
  11. agent_session_manager-0.6.0/src/asm/__init__.py +3 -0
  12. agent_session_manager-0.6.0/src/asm/__main__.py +54 -0
  13. agent_session_manager-0.6.0/src/asm/app.py +176 -0
  14. agent_session_manager-0.6.0/src/asm/i18n.py +467 -0
  15. agent_session_manager-0.6.0/src/asm/models.py +199 -0
  16. agent_session_manager-0.6.0/src/asm/screens/__init__.py +0 -0
  17. agent_session_manager-0.6.0/src/asm/screens/backups.py +713 -0
  18. agent_session_manager-0.6.0/src/asm/screens/codex_sessions.py +199 -0
  19. agent_session_manager-0.6.0/src/asm/screens/confirm.py +112 -0
  20. agent_session_manager-0.6.0/src/asm/screens/dashboard.py +402 -0
  21. agent_session_manager-0.6.0/src/asm/screens/debug_todos.py +633 -0
  22. agent_session_manager-0.6.0/src/asm/screens/file_history.py +348 -0
  23. agent_session_manager-0.6.0/src/asm/screens/input_dialog.py +71 -0
  24. agent_session_manager-0.6.0/src/asm/screens/migrate.py +474 -0
  25. agent_session_manager-0.6.0/src/asm/screens/projects.py +591 -0
  26. agent_session_manager-0.6.0/src/asm/services/__init__.py +0 -0
  27. agent_session_manager-0.6.0/src/asm/services/backup.py +593 -0
  28. agent_session_manager-0.6.0/src/asm/services/claude_data.py +877 -0
  29. agent_session_manager-0.6.0/src/asm/services/cleaner.py +334 -0
  30. agent_session_manager-0.6.0/src/asm/services/codex_data.py +368 -0
  31. agent_session_manager-0.6.0/src/asm/services/migrate.py +217 -0
  32. agent_session_manager-0.6.0/src/asm/services/pricing.py +141 -0
  33. agent_session_manager-0.6.0/src/asm/services/recovery.py +171 -0
  34. agent_session_manager-0.6.0/src/asm/services/update.py +107 -0
  35. agent_session_manager-0.6.0/src/asm/utils.py +12 -0
  36. agent_session_manager-0.6.0/src/asm/widgets/__init__.py +0 -0
  37. agent_session_manager-0.6.0/src/asm/widgets/action_bar.py +42 -0
  38. agent_session_manager-0.6.0/tests/__init__.py +0 -0
  39. agent_session_manager-0.6.0/tests/test_backup.py +415 -0
  40. agent_session_manager-0.6.0/tests/test_claude_data.py +151 -0
  41. agent_session_manager-0.6.0/tests/test_codex_data.py +89 -0
  42. agent_session_manager-0.6.0/tests/test_cross_platform.py +192 -0
  43. agent_session_manager-0.6.0/tests/test_feature_smoke.py +334 -0
  44. agent_session_manager-0.6.0/tests/test_integration_all.py +255 -0
  45. agent_session_manager-0.6.0/tests/test_update.py +78 -0
  46. agent_session_manager-0.6.0/uv.lock +868 -0
@@ -0,0 +1,39 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(find ~/.claude/projects -name *.jsonl -type f -size +1k)",
5
+ "Bash(python3:*)",
6
+ "Bash(ccusage --help)",
7
+ "Bash(pip show:*)",
8
+ "Bash(pipx list:*)",
9
+ "Bash(npm list:*)",
10
+ "Bash(npx ccusage:*)",
11
+ "Bash(git add:*)",
12
+ "Bash(git commit:*)",
13
+ "Bash(uv tool:*)",
14
+ "Bash(git push:*)",
15
+ "Bash(git checkout:*)",
16
+ "Bash(gh pr:*)",
17
+ "Bash(git pull:*)",
18
+ "Bash(find ~/.claude -name *mcp* -o -name .mcp.json)",
19
+ "Bash(source .venv/bin/activate)",
20
+ "Bash(python -m pytest tests/ -v)",
21
+ "Bash(python -m pytest tests/test_backup.py -v)",
22
+ "Bash(git worktree:*)",
23
+ "Bash(cp src/cc_tui/i18n.py ../worktree-backup-enhance/src/cc_tui/i18n.py)",
24
+ "Bash(cp src/cc_tui/models.py ../worktree-backup-enhance/src/cc_tui/models.py)",
25
+ "Bash(cp src/cc_tui/screens/backups.py ../worktree-backup-enhance/src/cc_tui/screens/backups.py)",
26
+ "Bash(cp src/cc_tui/services/backup.py ../worktree-backup-enhance/src/cc_tui/services/backup.py)",
27
+ "Bash(cp src/cc_tui/screens/input_dialog.py ../worktree-backup-enhance/src/cc_tui/screens/input_dialog.py)",
28
+ "Bash(git fetch:*)",
29
+ "Bash(git stash:*)",
30
+ "Bash(git branch:*)",
31
+ "Bash(cd:*)",
32
+ "Bash(git tag:*)",
33
+ "Bash(gh release:*)",
34
+ "Bash(rm -rf dist/)",
35
+ "Bash(python -m build)",
36
+ "Bash(python -m twine upload dist/*)"
37
+ ]
38
+ }
39
+ }
@@ -0,0 +1,13 @@
1
+ # Python-generated files
2
+ __pycache__/
3
+ *.py[oc]
4
+ build/
5
+ dist/
6
+ wheels/
7
+ *.egg-info
8
+
9
+ # Virtual environments
10
+ .venv
11
+
12
+ # Project
13
+ todo.md
@@ -0,0 +1 @@
1
+ 3.13
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 cc-tui contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,209 @@
1
+ Metadata-Version: 2.4
2
+ Name: agent-session-manager
3
+ Version: 0.6.0
4
+ Summary: Manage Claude Code & Codex sessions, cost, and data from your terminal
5
+ Project-URL: Homepage, https://github.com/Bae-ChangHyun/agent-session-manager
6
+ Project-URL: Repository, https://github.com/Bae-ChangHyun/agent-session-manager
7
+ Project-URL: Issues, https://github.com/Bae-ChangHyun/agent-session-manager/issues
8
+ Author-email: bch <bch@users.noreply.github.com>
9
+ License: MIT
10
+ License-File: LICENSE
11
+ Keywords: agent,claude,claude-code,codex,openai,session-manager,terminal,textual
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Operating System :: MacOS
17
+ Classifier: Operating System :: Microsoft :: Windows
18
+ Classifier: Operating System :: POSIX :: Linux
19
+ Classifier: Programming Language :: Python :: 3
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
24
+ Classifier: Topic :: Utilities
25
+ Classifier: Typing :: Typed
26
+ Requires-Python: >=3.11
27
+ Requires-Dist: claude-agent-sdk>=0.1.40
28
+ Requires-Dist: send2trash>=1.8.0
29
+ Requires-Dist: textual>=1.0.0
30
+ Description-Content-Type: text/markdown
31
+
32
+ <div align="center">
33
+
34
+ # agent-session-manager
35
+
36
+ **One terminal dashboard for everything Claude Code and Codex leave behind.**
37
+ See cost, browse sessions, and clean up `~/.claude` and `~/.codex` — side by side, with a Claude / Codex filter.
38
+
39
+ [![Python](https://img.shields.io/badge/Python-3.11%2B-3776AB?style=flat-square&logo=python&logoColor=white)](https://python.org)
40
+ [![Built with Textual](https://img.shields.io/badge/Built%20with-Textual-5A2CA0?style=flat-square)](https://github.com/Textualize/textual)
41
+ [![License](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](LICENSE)
42
+ [![Platform](https://img.shields.io/badge/Platform-Linux%20%7C%20macOS%20%7C%20Windows-orange?style=flat-square)](#)
43
+ [![Status](https://img.shields.io/badge/Status-Personal--use-lightgrey?style=flat-square)](#)
44
+
45
+ `asm` · **[한국어](README.ko.md)**
46
+
47
+ </div>
48
+
49
+ ---
50
+
51
+ > **⚠️ Heads up**
52
+ > This is a personal tool that reads and edits the **internal data of Claude Code and OpenAI Codex** (`~/.claude`, `~/.codex`). Every delete goes to the OS trash and is logged, but you should still back up before bulk operations. Not affiliated with Anthropic or OpenAI.
53
+
54
+ ---
55
+
56
+ ## What it is
57
+
58
+ If you live in **Claude Code** and **Codex**, the two of them quietly fill `~/.claude` and `~/.codex` with session transcripts, cost logs, debug files, task lists, and snapshots. After a few weeks it's impossible to tell which projects burned the most tokens, or which files are orphaned and safe to delete.
59
+
60
+ **agent-session-manager** (`asm`) puts all of it in one terminal dashboard. Both agents show up together, and a single keystroke filters the view to **All / Claude / Codex** so you can compare spend or drill into one tool.
61
+
62
+ ### 💡 Why it exists
63
+
64
+ - **Problem:** cost and session data for two different coding agents live in two opaque directories with no shared view.
65
+ - **Solution:** one TUI that reads both, prices every model accurately, and lets you clean up safely — to the trash, with recovery snapshots.
66
+
67
+ ---
68
+
69
+ ## Demo
70
+
71
+ The combined dashboard (cost across both agents, source-tagged rows), then the
72
+ same view filtered to **Claude** and to **Codex**:
73
+
74
+ <img src="docs/demo.gif" alt="asm demo" width="820"/>
75
+
76
+ ---
77
+
78
+ ## ✨ Features
79
+
80
+ ### Combined dashboard
81
+ - **Cost across both agents** with an in-app source filter — press `s` for All / Claude / Codex
82
+ - Per-model token & cost breakdown (Opus / Sonnet / Haiku / GPT-5.x), each row tagged by source
83
+ - Daily / weekly / monthly usage tables and a Top-10 project cost chart
84
+ - Accurate, current pricing from a LiteLLM-sourced rate table (new Opus/GPT models priced correctly, not at stale rates)
85
+
86
+ ### Claude management
87
+ - **Projects:** tree from `.claude.json`, expand to preview session conversations, trash sessions, remove projects from config
88
+ - **Orphan cleanup:** detect and bulk-clean sessions, file-history, debug, and task entries with no matching project
89
+ - **Duplicate sessions:** find the same session copied across projects and delete individual copies
90
+ - **Migration:** copy sessions between projects (originals preserved), with paths auto-rewritten
91
+
92
+ ### Codex sessions
93
+ - Browse `~/.codex` rollout sessions grouped by working directory
94
+ - Preview conversations and trash individual sessions
95
+
96
+ ### Safe by default
97
+ - Every delete goes to the **OS trash** and is recorded in an audit log
98
+ - **Recovery snapshots** are taken before trashing (Claude and Codex)
99
+ - Backups for Claude (config / settings / plugins / sessions / full) and Codex (sessions, excluding huge caches), with auto safety-backup and rollback on restore
100
+
101
+ ---
102
+
103
+ ## How it works
104
+
105
+ ```
106
+ ~/.claude ┐
107
+ ├──► asm ──► one dashboard ──► filter: All / Claude / Codex
108
+ ~/.codex ┘ (cost · sessions · cleanup · backup)
109
+ ```
110
+
111
+ `asm` reads both data directories directly — no daemon, no config. Claude data is grouped by project; Codex sessions are grouped by working directory. Pricing is computed from each session's recorded token usage.
112
+
113
+ | Path | What's there |
114
+ |:---|:---|
115
+ | `~/.claude.json` · `~/.claude/projects/` | Claude projects, costs, session JSONL |
116
+ | `~/.claude/file-history/` · `debug/` · `tasks/` | Snapshots, debug logs, per-session task lists |
117
+ | `~/.codex/sessions/` | Codex rollout session files |
118
+ | `~/.asm/backups/` · `trash-log.jsonl` | Backups (auto-migrated from old `~/.cc-tui`) and the deletion audit log |
119
+
120
+ ---
121
+
122
+ ## 🛠️ Tech Stack
123
+
124
+ - **TUI:** [Textual](https://github.com/Textualize/textual) + [Rich](https://github.com/Textualize/rich)
125
+ - **Safety:** [send2trash](https://github.com/arsenetar/send2trash) (OS trash, not `rm`)
126
+ - **Sessions:** [claude-agent-sdk](https://pypi.org/project/claude-agent-sdk/) with a JSONL fallback parser
127
+ - **Python:** 3.11+
128
+
129
+ ---
130
+
131
+ ## 🚀 Getting started
132
+
133
+ ### Install (recommended)
134
+
135
+ ```bash
136
+ # uv
137
+ uv tool install agent-session-manager
138
+
139
+ # pip
140
+ pip install agent-session-manager
141
+ ```
142
+
143
+ Both install a single `asm` command. On first run, data from the previous `~/.cc-tui` location is migrated to `~/.asm` automatically.
144
+
145
+ <details>
146
+ <summary><strong>Run from source</strong></summary>
147
+
148
+ ```bash
149
+ git clone https://github.com/Bae-ChangHyun/agent-session-manager.git
150
+ cd agent-session-manager
151
+ uv sync && uv run asm
152
+ ```
153
+
154
+ </details>
155
+
156
+ ### Usage
157
+
158
+ ```bash
159
+ asm # Launch — shows Claude + Codex together
160
+ asm --source codex # Start with the dashboard filtered to Codex
161
+ asm --path /your/project # Limit to one Claude project
162
+ asm --lang ko # Korean UI (or set ASM_LANG=ko)
163
+ asm --no-update-check # Skip the startup update check
164
+ ```
165
+
166
+ Both sources are always available; `--source` only sets the dashboard's initial filter, which you change anytime with `s`.
167
+
168
+ ### Keyboard
169
+
170
+ | Key | Action |
171
+ |:---:|:---|
172
+ | `F1`–`F6` | Switch tabs |
173
+ | `s` | Dashboard source filter (All / Claude / Codex) |
174
+ | `Tab` / `Shift+Tab` · `1` `2` `3` | Dashboard period (Daily / Weekly / Monthly) |
175
+ | `d` / `D` | Trash selected / all orphaned |
176
+ | `Space` · `Enter` | Toggle selection · Preview conversation |
177
+ | `r` · `q` | Refresh all · Quit |
178
+
179
+ ### Staying up to date
180
+
181
+ When a newer release is on PyPI, `asm` offers a `y/N` upgrade prompt on startup (via `uv tool` or `pip`). It's skipped in non-interactive shells and when offline.
182
+
183
+ ---
184
+
185
+ ## 🗺️ Roadmap
186
+
187
+ - [ ] Codex session **restore** (backup / list / delete exist today; restore does not)
188
+ - [ ] Per-source disk-usage and retention hints in the data overview
189
+ - [ ] Publish to PyPI as `agent-session-manager`
190
+
191
+ ---
192
+
193
+ ## ⚠️ Status & scope
194
+
195
+ - **Personal / pre-release**, under active development.
196
+ - Operates directly on Claude Code and Codex internal data — **back up before bulk deletes**.
197
+ - Deletes go to the OS trash with recovery snapshots; nothing is `rm`'d in place.
198
+ - No warranty. Not affiliated with Anthropic or OpenAI.
199
+
200
+ ---
201
+
202
+ ## 📄 License
203
+
204
+ [MIT](LICENSE)
205
+
206
+ <div align="center">
207
+ <br/>
208
+ Made with <b>Claude Code</b> · and now <b>Codex</b> too
209
+ </div>
@@ -0,0 +1,360 @@
1
+ # CC-TUI 개발 기획 문서
2
+
3
+ > Claude Code 세션 데이터 관리를 위한 터미널 UI 도구의 개발 여정 기록
4
+
5
+ ---
6
+
7
+ ## 1. 프로젝트 동기와 배경
8
+
9
+ ### 문제 인식
10
+
11
+ Claude Code를 일상적으로 사용하다 보면 `~/.claude/` 디렉토리에 데이터가 조용히 쌓인다. 세션 대화 파일(`.jsonl`), 파일 히스토리 스냅샷, 디버그 로그, 내부 Todo 메모 등이 그것이다. 문제는 이 데이터들이 **가시성 없이** 쌓인다는 점이다.
12
+
13
+ - 어떤 프로젝트가 얼마나 비용을 썼는지 알 수 없다
14
+ - 프로젝트를 이동하거나 삭제하면 연관 데이터가 "orphan" 상태로 남는다
15
+ - 세션 수백 개가 쌓여도 어느 세션이 무슨 작업이었는지 한눈에 보이지 않는다
16
+ - 특히 laptop을 옮기거나 프로젝트 경로가 바뀌면 세션 데이터를 새 경로로 이전할 방법이 없다
17
+
18
+ Claude Code의 내부 데이터 구조는 경로를 인코딩한 디렉토리명(`-home-bch-Project-...` 형태)을 사용한다. 사람이 직접 관리하기 어려운 구조다.
19
+
20
+ ### 해결 방향
21
+
22
+ 터미널에서 바로 실행할 수 있는 TUI(Terminal User Interface) 도구를 만들기로 했다. GUI 앱이 아닌 TUI를 선택한 이유는 간단하다. Claude Code 자체가 터미널 도구이고, 그 데이터를 관리하는 도구도 같은 컨텍스트에서 동작해야 자연스럽다.
23
+
24
+ ---
25
+
26
+ ## 2. 기술 스택 선택
27
+
28
+ ### 왜 Textual인가
29
+
30
+ Python TUI 라이브러리 중 Textual을 선택한 결정적인 이유는 세 가지다.
31
+
32
+ **1. 컴포넌트 기반 UI 모델**
33
+ Textual은 React와 유사한 컴포넌트 모델을 제공한다. `Widget` 단위로 UI를 구성하고, CSS로 스타일을 별도 관리할 수 있다. 복잡한 탭 레이아웃, 트리 뷰, 테이블, 모달 다이얼로그를 조합하는 이 프로젝트에 적합했다.
34
+
35
+ **2. 비동기 워커 지원**
36
+ 데이터 로딩이 UI를 블로킹해선 안 된다. `run_worker(thread=True)`와 `call_from_thread()`를 통해 백그라운드 스레드에서 파일시스템/SDK 작업을 수행하고 결과를 UI 스레드로 안전하게 전달하는 패턴을 제공한다.
37
+
38
+ **3. Rich 마크업 통합**
39
+ Textual은 Rich 라이브러리를 기반으로 한다. `[bold]`, `[green]`, `[yellow]` 등의 Rich 마크업을 `Static` 위젯에 직접 쓸 수 있어 별도 위젯 없이도 시각적으로 풍부한 인터페이스를 만들 수 있다.
40
+
41
+ ### 핵심 의존성
42
+
43
+ | 라이브러리 | 역할 |
44
+ |-----------|------|
45
+ | `textual >= 1.0.0` | TUI 프레임워크 |
46
+ | `send2trash >= 1.8.0` | 안전한 파일 삭제 (휴지통으로 이동) |
47
+ | `claude-agent-sdk` | Claude 세션 메타데이터 조회 |
48
+
49
+ `send2trash`를 선택한 이유는 중요하다. 세션 데이터를 실수로 영구 삭제하면 복구가 불가능하다. `os.remove()`나 `shutil.rmtree()` 대신 OS 휴지통으로 이동하는 방식을 선택해서 안전망을 확보했다.
50
+
51
+ ---
52
+
53
+ ## 3. 아키텍처
54
+
55
+ ### 디렉토리 구조
56
+
57
+ ```
58
+ src/cc_tui/
59
+ ├── __main__.py # 진입점, CLI 인자 파싱
60
+ ├── app.py # CCTuiApp: 탭 레이아웃, 전역 키바인딩
61
+ ├── models.py # 데이터 모델, 경로 상수, 인코딩 유틸
62
+ ├── i18n.py # 국제화 (한국어/영어)
63
+ ├── screens/ # UI 화면(탭) 모음
64
+ │ ├── dashboard.py # 대시보드: 비용/사용량 통계
65
+ │ ├── projects.py # 프로젝트 트리 + 세션 관리
66
+ │ ├── sessions.py # 전체 세션 목록 (시간순)
67
+ │ ├── file_history.py # 파일 히스토리 관리
68
+ │ ├── debug_todos.py # 디버그/Todo 파일 관리
69
+ │ ├── migrate.py # 세션 마이그레이션
70
+ │ ├── backups.py # 백업/복원
71
+ │ ├── orphaned.py # Orphan 데이터 관리
72
+ │ └── confirm.py # 확인 모달 다이얼로그
73
+ └── services/ # 비즈니스 로직 레이어
74
+ ├── claude_data.py # 데이터 읽기 (SDK + JSONL fallback)
75
+ ├── cleaner.py # 파일 삭제 (send2trash 래퍼)
76
+ ├── backup.py # 백업/복원
77
+ └── migrate.py # 세션 마이그레이션 로직
78
+ ```
79
+
80
+ ### 레이어 분리 원칙
81
+
82
+ UI 레이어(`screens/`)와 데이터 레이어(`services/`)를 명확히 분리했다. `screens/`는 Textual 위젯만 다루고, 실제 파일시스템 접근과 데이터 처리는 모두 `services/`에서 담당한다. 이 구조 덕분에 UI 변경이 로직에 영향을 주지 않고, 서비스 로직을 독립적으로 테스트할 수 있다.
83
+
84
+ ### Claude 데이터 구조 이해
85
+
86
+ Claude Code는 프로젝트 경로를 다음 규칙으로 인코딩해서 디렉토리명으로 사용한다.
87
+
88
+ ```
89
+ /home/bch/Project/my-app
90
+ → -home-bch-Project-my-app
91
+ ```
92
+
93
+ 구체적으로는 알파벳/숫자 이외의 문자를 모두 `-`로 치환한다. `models.py`의 `encode_path()`가 이 로직을 구현한다.
94
+
95
+ 이 인코딩은 단방향이다. `-home-bch-Project-my-app`을 다시 `/home/bch/Project/my-app`으로 완벽하게 복원할 수 없다 (원래 문자가 `/`인지 `-`인지 알 수 없다). 따라서 `decode_path_hint()`는 "힌트" 수준의 표시용 디코딩만 제공한다.
96
+
97
+ ### SDK + JSONL Fallback 패턴
98
+
99
+ 세션 데이터 조회는 두 경로로 구현했다.
100
+
101
+ ```python
102
+ def get_session_details(project_dir):
103
+ try:
104
+ return _get_sessions_via_sdk(project_dir) # 우선
105
+ except Exception:
106
+ return _get_sessions_via_jsonl(project_dir) # fallback
107
+ ```
108
+
109
+ `claude-agent-sdk`가 설치되어 있고 동작하면 SDK를 사용하고, 실패하면 `.jsonl` 파일을 직접 파싱한다. SDK 의존성이 미래에 깨질 경우에도 동작을 보장하는 방어적 설계다.
110
+
111
+ ---
112
+
113
+ ## 4. 주요 기술적 결정사항
114
+
115
+ ### 4.1 Rich markup 기반 액션바
116
+
117
+ Textual에서 클릭 가능한 버튼을 여러 개 나란히 배치하려면 `Button` 위젯을 여러 개 쓰거나 `Horizontal` 컨테이너를 쓰는 것이 일반적이다. 하지만 이 프로젝트는 다른 방식을 선택했다.
118
+
119
+ `Static` 위젯 하나에 Rich 마크업으로 색깔 있는 버튼처럼 보이는 텍스트를 렌더링하고, 클릭 이벤트의 `x` 좌표를 사전에 계산해둔 클릭 맵과 대조해서 어떤 "버튼"이 눌렸는지 판단한다.
120
+
121
+ ```python
122
+ def _build_action_bar(self, actions):
123
+ parts = []
124
+ click_map = []
125
+ x = 0
126
+ for action_id, label, color in actions:
127
+ text = f" {label} "
128
+ width = cell_len(text) # Rich cell 너비 계산
129
+ click_map.append((x, x + width, action_id))
130
+ parts.append(f"[bold white on {color}]{text}[/]")
131
+ x += width
132
+ return " ".join(parts), click_map
133
+ ```
134
+
135
+ 이 방식을 선택한 이유는 레이아웃 유연성이다. 표시할 버튼 수와 라벨이 동적으로 변한다 (예: orphan이 없으면 "Orphan 전체 삭제" 버튼이 사라진다). 위젯 수가 동적으로 변하는 것보다 텍스트를 동적으로 생성하는 것이 Textual에서 훨씬 간단하다.
136
+
137
+ ### 4.2 Orphan 감지 로직
138
+
139
+ "Orphan"은 `.claude.json`에 등록된 프로젝트와 매칭되지 않는 데이터를 의미한다. 이 감지는 두 가지 방향으로 구현된다.
140
+
141
+ **세션 디렉토리 orphan 감지**: `~/.claude/projects/` 아래 각 디렉토리명을 `.claude.json`의 프로젝트 경로 인코딩 결과와 대조한다.
142
+
143
+ ```python
144
+ for pp in project_paths:
145
+ if encode_path(pp) == actual_path:
146
+ is_orphaned = False
147
+ break
148
+ ```
149
+
150
+ **파일 히스토리/디버그/Todo orphan 감지**: 파일명에서 session ID를 추출하고, 현재 존재하는 모든 세션 ID 집합과 대조한다.
151
+
152
+ ```python
153
+ def _get_active_session_ids() -> set[str]:
154
+ ids = set()
155
+ for d in PROJECTS_DIR.iterdir():
156
+ for jsonl in d.glob("*.jsonl"):
157
+ ids.add(jsonl.stem)
158
+ return ids
159
+ ```
160
+
161
+ Todo 파일명은 `{UUID}-agent-{UUID}.json` 형태라 앞부분 UUID만 추출해서 세션 ID로 사용한다.
162
+
163
+ ### 4.3 프로젝트 트리 경로 압축
164
+
165
+ 프로젝트가 `/home/bch/Project/backend/api` 처럼 깊은 경로에 있을 때, 중간 경로 노드를 하나씩 다 펼치면 트리가 너무 깊어진다. 자식이 하나뿐인 중간 디렉토리는 자동으로 압축해서 `home/bch/Project/backend` 를 하나의 노드로 표시한다.
166
+
167
+ ```python
168
+ elif len(child_dirs) == 1 and not is_project:
169
+ # Single child dir - collapse into parent label
170
+ collapsed_label = f"{key}/{child_key}"
171
+ while 단일_자식_체인이_계속되면:
172
+ collapsed_label = f"{collapsed_label}/{next_key}"
173
+ ```
174
+
175
+ ### 4.4 비동기 데이터 로딩
176
+
177
+ 대시보드의 일별/주별/월별 사용량 데이터는 수백 개의 JSONL 파일을 파싱해야 한다. 이 작업을 메인 스레드에서 하면 UI가 멈춘다.
178
+
179
+ 구현 전략:
180
+ 1. 앱 시작 시 일별(daily) 데이터를 먼저 로드해서 즉시 표시
181
+ 2. 주별(weekly)/월별(monthly) 데이터는 백그라운드 워커로 추가 로드
182
+ 3. 로드 완료 시 `call_from_thread()`로 UI 업데이트
183
+ 4. 한번 로드한 데이터는 `_cached_periods` 딕셔너리에 캐시
184
+
185
+ ```python
186
+ def _load(self) -> None:
187
+ daily = get_period_usage("daily")
188
+ self.app.call_from_thread(self._on_data_loaded, stats, usage, {"daily": daily})
189
+ # 백그라운드에서 추가 로드
190
+ for key in ("weekly", "monthly"):
191
+ data = get_period_usage(key)
192
+ self.app.call_from_thread(self._on_bg_period_loaded, key, data)
193
+ ```
194
+
195
+ ### 4.5 세션 마이그레이션 경로 재작성
196
+
197
+ 프로젝트를 `A` 경로에서 `B` 경로로 이전할 때, 세션 파일 내부에 기록된 `cwd` 경로도 함께 업데이트해야 한다. `migrate.py`의 `_update_paths()`가 복사된 JSONL 파일의 텍스트를 치환하는 방식으로 이를 처리한다.
198
+
199
+ ---
200
+
201
+ ## 5. 구현된 기능 상세
202
+
203
+ ### 탭 1: 대시보드 (Dashboard)
204
+
205
+ Claude Code 사용 현황을 한눈에 파악하는 화면이다.
206
+
207
+ **표시 정보:**
208
+ - 최초 사용일, 총 시작 횟수, 총 세션 수
209
+ - 모델별 누적 비용 및 토큰 사용량 (claude-sonnet, claude-haiku 등)
210
+ - 일별/주별/월별 사용량 집계 테이블 (클릭으로 전환)
211
+ - 비용 상위 10개 프로젝트 (막대 그래프 형식)
212
+ - 데이터 개요: 프로젝트 수, 세션 수, orphan 수, 디스크 사용량
213
+
214
+ **데이터 소스:**
215
+ - `~/.claude.json`: 프로젝트별 lastCost, lastModelUsage
216
+ - `~/.claude/history.jsonl`: 세션 시작 이력
217
+ - `~/.claude/projects/**/*.jsonl`: 어시스턴트 메시지의 usage 필드 파싱
218
+
219
+ 비용 집계는 Claude 공개 요금표 기준으로 직접 계산한다 (Sonnet: input $3/1M, output $15/1M 등).
220
+
221
+ ### 탭 2: 프로젝트 (Projects)
222
+
223
+ `.claude.json`에 등록된 모든 프로젝트를 계층적 트리로 표시하는 화면이다.
224
+
225
+ **주요 기능:**
226
+ - 프로젝트 경로를 파일시스템 구조 그대로 트리로 표시
227
+ - 각 프로젝트 옆에 [O]=폴더 존재, [X]=폴더 없음(삭제/이동됨) 표시
228
+ - 프로젝트를 펼치면 세션 목록이 최신순으로 표시
229
+ - 세션 선택 시 우측 패널에 대화 내용 미리보기
230
+ - 세션 개별 삭제 (휴지통으로 이동)
231
+ - 세션 없는 프로젝트를 `.claude.json` 목록에서 제거
232
+ - Orphan 세션 디렉토리 일괄 삭제 액션바
233
+
234
+ **설계 포인트:** 세션 목록은 프로젝트 노드를 펼칠 때 lazy하게 로드한다. placeholder 노드를 먼저 추가해두고, 실제 데이터가 로드되면 교체하는 방식이다.
235
+
236
+ ### 탭 3: 파일 히스토리 (File History)
237
+
238
+ `~/.claude/file-history/`에 저장된 파일 버전 스냅샷을 관리하는 화면이다.
239
+
240
+ Claude Code가 파일을 수정하기 전에 원본을 이 디렉토리에 저장한다. "파일 되돌리기" 기능의 백엔드다. 오래된 스냅샷은 정리해도 안전하지만, 활성 프로젝트에 연결된 것은 삭제 시 롤백이 불가능해진다.
241
+
242
+ **표시 방식:** orphan 항목을 상단에 강조 표시하고, 항목 선택 시 어느 프로젝트/세션에 연결된 것인지 상세 정보를 우측 패널에 표시한다.
243
+
244
+ ### 탭 4: 디버그/Todos (Debug/Todos)
245
+
246
+ `~/.claude/debug/`와 `~/.claude/todos/`의 내부 파일들을 관리하는 화면이다.
247
+
248
+ 이 파일들은 Claude Code가 내부적으로 생성하는 것으로, 사용자의 Todo가 아니다.
249
+
250
+ - **Debug 파일**: Claude Code 오류 발생 시 생성되는 내부 로그. 버그 리포트 외에는 불필요하다.
251
+ - **Todo 파일**: Claude가 세션 중 내부 작업 추적용으로 생성하는 메모. 세션 종료 후 불필요하다.
252
+
253
+ **추가 기능:**
254
+ - 빈 파일 감지 및 일괄 정리 (`[]`, `{}`, 빈 문자열)
255
+ - 선택 항목의 파일 내용 우측 패널에서 미리보기
256
+ - 항목을 프로젝트별로 그룹화해서 표시
257
+
258
+ ### 탭 5: 마이그레이션 (Migrate)
259
+
260
+ 프로젝트 A의 세션 데이터를 프로젝트 B로 복사하는 기능이다.
261
+
262
+ **사용 사례:** 프로젝트를 다른 경로로 이전했을 때, 기존 경로의 세션들을 새 경로로 옮기고 싶은 경우.
263
+
264
+ **UI 설계:** 좌우 두 패널로 구성되어 있다. 왼쪽에서 소스 프로젝트를, 오른쪽에서 대상 프로젝트를 선택한다. 소스 선택 시 하단에 세션 미리보기가 표시된다.
265
+
266
+ **동작 모드:**
267
+ - **Append**: 기존 세션을 유지하고 새 세션만 추가 (중복 스킵)
268
+ - **Overwrite**: 기존 세션을 모두 삭제하고 소스 세션으로 교체
269
+
270
+ 세션 파일 복사 후 내부에 기록된 경로 문자열도 자동으로 업데이트한다.
271
+
272
+ ### 탭 6: 백업 (Backups)
273
+
274
+ `~/.cc-tui/backups/`에 백업을 생성하고 복원하는 기능이다.
275
+
276
+ **백업 종류:**
277
+ - **설정 백업**: `.claude.json`만 복사 (빠르고 가벼움)
278
+ - **전체 백업**: `.claude/` 디렉토리 전체와 `.claude.json`을 통째로 복사
279
+
280
+ 세션 데이터 삭제나 마이그레이션 전에 미리 백업을 생성해두면 실수해도 복구할 수 있다. 복원 시에는 현재 설정을 먼저 자동 백업한 뒤 복원을 진행해서 복원 작업 자체도 되돌릴 수 있게 했다.
281
+
282
+ ### 공통: 확인 모달 (ConfirmScreen)
283
+
284
+ 모든 삭제/수정 작업은 `ConfirmScreen` 모달을 통해 확인을 거친다. `ModalScreen[bool]`을 상속해서 타입 안전한 결과 반환을 구현했다.
285
+
286
+ ### 국제화 (i18n)
287
+
288
+ 모든 UI 문자열을 `i18n.py`의 `TRANSLATIONS` 딕셔너리에서 관리한다. `--lang ko` CLI 옵션 또는 `CC_TUI_LANG=ko` 환경변수로 한국어로 전환할 수 있다.
289
+
290
+ ```bash
291
+ cc-tui --lang ko
292
+ # 또는
293
+ CC_TUI_LANG=ko cc-tui
294
+ ```
295
+
296
+ ---
297
+
298
+ ## 6. Claude Code 데이터 구조 참고
299
+
300
+ cc-tui가 다루는 경로들:
301
+
302
+ ```
303
+ ~/.claude.json # 프로젝트 목록, 비용, 모델 사용량
304
+ ~/.claude/
305
+ ├── projects/ # 세션 데이터
306
+ │ └── -home-bch-Project-app/ # 경로 인코딩된 디렉토리
307
+ │ ├── {session-uuid}.jsonl # 대화 기록
308
+ │ ├── sessions-index.json # 세션 인덱스
309
+ │ └── memory/ # 프로젝트 메모리
310
+ ├── file-history/ # 파일 버전 스냅샷
311
+ ├── debug/ # 내부 디버그 로그
312
+ ├── todos/ # 내부 Todo 메모
313
+ ├── session-env/ # 세션 환경 변수
314
+ └── history.jsonl # 세션 시작 이력
315
+ ~/.cc-tui/backups/ # cc-tui 백업 저장소
316
+ ```
317
+
318
+ ---
319
+
320
+ ## 7. 앞으로의 계획 (로드맵)
321
+
322
+ ### 단기 (v0.2)
323
+
324
+ - **세션 검색**: 대화 내용 키워드 검색
325
+ - **비용 알림**: 일/월 사용량이 임계값 초과 시 알림
326
+ - **설정 파일**: YAML/TOML 기반 사용자 설정 (알림 임계값, 기본 언어 등)
327
+ - **Sessions 탭 완성**: 현재 `sessions.py`가 구현되어 있으나 앱에 아직 탭으로 노출되지 않음
328
+
329
+ ### 중기 (v0.3)
330
+
331
+ - **자동 정리 규칙**: "30일 이상 된 orphan 자동 정리" 같은 규칙 설정
332
+ - **사용량 내보내기**: CSV/JSON으로 사용량 데이터 내보내기
333
+ - **세션 태그/메모**: 세션에 사용자 정의 태그 붙이기
334
+ - **디스크 사용량 분석**: 어느 프로젝트가 공간을 많이 차지하는지 시각화
335
+
336
+ ### 장기 (v1.0)
337
+
338
+ - **패키지 배포**: PyPI에 `cc-tui` 패키지 배포
339
+ - **다중 Claude 인스턴스 지원**: 팀 내 여러 사용자의 데이터 통합 뷰
340
+ - **MCP 서버 관리**: 프로젝트별 MCP 서버 설정 시각화 및 관리
341
+
342
+ ---
343
+
344
+ ## 8. 개발 시 트레이드오프
345
+
346
+ ### 버튼 vs 텍스트 액션바
347
+
348
+ Textual `Button` 위젯을 쓰면 접근성과 포커스 관리가 자동으로 된다. 하지만 동적 개수의 버튼을 수평 배치하고 조건부로 표시/숨기는 것은 위젯 추가/제거가 필요해서 복잡해진다. Rich 마크업 기반 클릭 맵은 단순하지만 키보드 접근성이 없다는 단점이 있다. 현재는 마우스 클릭 중심 사용 패턴을 가정하고 후자를 선택했다.
349
+
350
+ ### SDK 의존 vs 직접 파싱
351
+
352
+ `claude-agent-sdk`가 편리하지만 Claude Code의 내부 API이므로 언제든 변경될 수 있다. JSONL 직접 파싱은 안정적이지만 SDK가 제공하는 메타데이터(summary, git_branch 등)를 얻기 어렵다. SDK 우선, JSONL fallback 구조가 현재로선 최선의 타협이다.
353
+
354
+ ### 실시간 감시 vs 수동 새로고침
355
+
356
+ `watchdog` 같은 파일시스템 감시 라이브러리로 데이터 변경을 실시간 감지할 수 있다. 하지만 Claude Code가 세션을 진행하는 동안 계속 업데이트되는 파일을 실시간으로 읽으면 I/O 경합이 발생한다. `r` 키 수동 새로고침을 선택한 것은 이 때문이다.
357
+
358
+ ---
359
+
360
+ *최초 작성: 2026-03-13*