claude-session-backup 0.4.0__tar.gz → 0.4.2__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 (67) hide show
  1. claude_session_backup-0.4.2/PKG-INFO +256 -0
  2. claude_session_backup-0.4.2/README.md +224 -0
  3. {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/claude_session_backup/_version.py +2 -2
  4. {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/claude_session_backup/commands.py +29 -33
  5. {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/claude_session_backup/config.py +38 -3
  6. claude_session_backup-0.4.2/claude_session_backup.egg-info/PKG-INFO +256 -0
  7. {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/tests/test_config.py +64 -0
  8. {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/tests/test_restore.py +81 -0
  9. claude_session_backup-0.4.0/PKG-INFO +0 -356
  10. claude_session_backup-0.4.0/README.md +0 -324
  11. claude_session_backup-0.4.0/claude_session_backup.egg-info/PKG-INFO +0 -356
  12. {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/LICENSE +0 -0
  13. {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/claude_session_backup/__init__.py +0 -0
  14. {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/claude_session_backup/__main__.py +0 -0
  15. {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/claude_session_backup/cli.py +0 -0
  16. {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/claude_session_backup/distill.py +0 -0
  17. {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/claude_session_backup/fts5_db.py +0 -0
  18. {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/claude_session_backup/fts5_importer.py +0 -0
  19. {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/claude_session_backup/fts5_index.py +0 -0
  20. {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/claude_session_backup/fts5_migrations.py +0 -0
  21. {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/claude_session_backup/fts_paths.py +0 -0
  22. {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/claude_session_backup/git_ops.py +0 -0
  23. {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/claude_session_backup/ids.py +0 -0
  24. {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/claude_session_backup/index.py +0 -0
  25. {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/claude_session_backup/lockfile.py +0 -0
  26. {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/claude_session_backup/metadata.py +0 -0
  27. {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/claude_session_backup/migrations.py +0 -0
  28. {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/claude_session_backup/pathkit.py +0 -0
  29. {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/claude_session_backup/scanner.py +0 -0
  30. {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/claude_session_backup/search.py +0 -0
  31. {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/claude_session_backup/search_render.py +0 -0
  32. {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/claude_session_backup/sesslog_parser.py +0 -0
  33. {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/claude_session_backup/sesslog_scanner.py +0 -0
  34. {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/claude_session_backup/timeline.py +0 -0
  35. {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/claude_session_backup/transcript_walker.py +0 -0
  36. {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/claude_session_backup.egg-info/SOURCES.txt +0 -0
  37. {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/claude_session_backup.egg-info/dependency_links.txt +0 -0
  38. {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/claude_session_backup.egg-info/entry_points.txt +0 -0
  39. {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/claude_session_backup.egg-info/requires.txt +0 -0
  40. {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/claude_session_backup.egg-info/top_level.txt +0 -0
  41. {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/pyproject.toml +0 -0
  42. {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/setup.cfg +0 -0
  43. {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/tests/test_backup_hook.py +0 -0
  44. {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/tests/test_cli.py +0 -0
  45. {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/tests/test_commands.py +0 -0
  46. {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/tests/test_distill.py +0 -0
  47. {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/tests/test_fts5_db.py +0 -0
  48. {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/tests/test_fts5_importer.py +0 -0
  49. {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/tests/test_fts5_index.py +0 -0
  50. {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/tests/test_fts5_migrations.py +0 -0
  51. {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/tests/test_fts_paths.py +0 -0
  52. {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/tests/test_hook.py +0 -0
  53. {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/tests/test_ids.py +0 -0
  54. {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/tests/test_index.py +0 -0
  55. {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/tests/test_lockfile.py +0 -0
  56. {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/tests/test_metadata.py +0 -0
  57. {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/tests/test_migrations.py +0 -0
  58. {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/tests/test_pathkit.py +0 -0
  59. {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/tests/test_scanner.py +0 -0
  60. {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/tests/test_search.py +0 -0
  61. {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/tests/test_search_render.py +0 -0
  62. {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/tests/test_session_sources.py +0 -0
  63. {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/tests/test_sesslog_parser.py +0 -0
  64. {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/tests/test_sesslog_scanner.py +0 -0
  65. {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/tests/test_timeline.py +0 -0
  66. {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/tests/test_version.py +0 -0
  67. {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/tests/test_view.py +0 -0
@@ -0,0 +1,256 @@
1
+ Metadata-Version: 2.4
2
+ Name: claude-session-backup
3
+ Version: 0.4.2
4
+ Summary: Git-backed Claude Code session backup with timeline view, folder analysis, deletion detection, and session restore.
5
+ Author-email: djdarcy <6962246+djdarcy@users.noreply.github.com>
6
+ License: GPL-3.0-or-later
7
+ Project-URL: Homepage, https://github.com/DazzleML/Claude-Session-Backup
8
+ Project-URL: Repository, https://github.com/DazzleML/Claude-Session-Backup
9
+ Project-URL: Issues, https://github.com/DazzleML/Claude-Session-Backup/issues
10
+ Keywords: claude,claude-code,session,backup,restore,git,archive,conversation
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: System :: Archiving :: Backup
22
+ Classifier: Topic :: Utilities
23
+ Requires-Python: >=3.10
24
+ Description-Content-Type: text/markdown
25
+ License-File: LICENSE
26
+ Requires-Dist: rich>=13.0.0
27
+ Requires-Dist: dazzle-filekit>=0.2.4
28
+ Provides-Extra: dev
29
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
30
+ Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
31
+ Dynamic: license-file
32
+
33
+ # Claude-Session-Backup
34
+
35
+ [![PyPI](https://img.shields.io/pypi/v/claude-session-backup?color=green)](https://pypi.org/project/claude-session-backup/)
36
+ [![Release Date](https://img.shields.io/github/release-date/DazzleML/Claude-Session-Backup?color=green)](https://github.com/DazzleML/Claude-Session-Backup/releases)
37
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
38
+ [![License: GPL v3](https://img.shields.io/badge/license-GPL%20v3-green.svg)](https://www.gnu.org/licenses/gpl-3.0.html)
39
+ [![Installs](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/djdarcy/7aa669e4d85856079eacc71f88c58f6b/raw/installs.json)](https://dazzleml.github.io/Claude-Session-Backup/stats/#installs)
40
+ [![Platform](https://img.shields.io/badge/platform-Windows%20%7C%20Linux%20%7C%20macOS%20%7C%20BSD-lightgrey.svg)](docs/platforms.md)
41
+
42
+ **Git-backed Claude Code session backup with timeline view, folder analysis, deletion detection, and session restore.**
43
+
44
+ ## The Problem
45
+
46
+ Claude Code stores session data in `~/.claude/projects/` as JSONL files. These can be silently deleted during upgrades, lossy-compacted via `/compact`, or lost when session compatibility breaks between versions. Once gone, your conversation history -- including debugging sessions, architectural decisions, and code review context -- is unrecoverable.
47
+
48
+ **csb** preserves every session in your existing `~/.claude` git repository, builds a searchable metadata index, detects deletions, and can restore lost sessions from git history.
49
+
50
+ > [!NOTE]
51
+ > **Alpha software (cusp of beta) -- nearly feature-complete.** Everything on the original roadmap has shipped: backup, deletion detection, content search (JSONL+sesslogs+FTS5), full session restore (a deleted session's complete footprint recovered from git -- transcript, subagents, tool-results, logger sesslogs -- with original timestamps and symlinks, resumable in Claude Code), a viewer launcher (`csb view`), and a human-readable chat-log layer (`csb distill`).
52
+ >
53
+ > Staying alpha a little longer while the tires get kicked; beta follows once v0.4.0 has settled. Expect occasional rough edges and breaking changes between minor versions until then. By all means use it (and please [file issues](https://github.com/DazzleML/Claude-Session-Backup/issues)) but, as with any backup tool, keep a second copy of anything irreplaceable.
54
+
55
+ ## Quick Start
56
+
57
+ Three commands install everything -- the CLI plus the Claude Code plugin that fires backups automatically on PreCompact and SessionEnd:
58
+
59
+ ```bash
60
+ # 1. Install the csb CLI
61
+ pip install claude-session-backup
62
+
63
+ # 2. Add the DazzleML marketplace (one-time)
64
+ claude plugin marketplace add "DazzleML/Claude-Session-Backup"
65
+
66
+ # 3. Install the plugin -- registers the PreCompact + SessionEnd hooks
67
+ claude plugin install claude-session-backup@dazzle-claude-session-backup
68
+ ```
69
+
70
+ Then verify it works:
71
+
72
+ ```bash
73
+ # Build the index from existing sessions (no git commits yet)
74
+ csb backup --no-commit
75
+
76
+ # See your session timeline
77
+ csb list
78
+
79
+ # Full backup with git commits (separate noise + user commits, unsigned)
80
+ csb backup
81
+ ```
82
+
83
+ > [!TIP]
84
+ > **Pair with [claude-session-logger](https://github.com/DazzleML/claude-session-logger/)** for full searchable history. csb preserves Claude Code's session transcripts (`projects/<slug>/<uuid>.jsonl`). The logger captures the *richer* per-session data alongside them -- tool calls, shell commands, agent dispatches -- written to `~/.claude/sesslogs/`. csb backs up those logger files too (it backs up everything under `~/.claude/` via the noise commits), and `csb restore` brings the whole footprint back together: transcript + subagents + tool-results + logger state + sesslogs. The two projects are independent (csb works fine without the logger) but they're designed to complement each other.
85
+
86
+ ## Features
87
+
88
+ - **Full session preservation**: Every byte of JSONL, subagent data, tool results backed up via git
89
+ - **Timeline view**: Sessions sorted by last use with relative dates, start folder, and top N working directories
90
+ - **Folder analysis**: See where work actually happened -- the most-used folder is highlighted
91
+ - **Deletion detection**: Know when Claude Code removes a session you previously tracked
92
+ - **Session restore**: Recover deleted sessions from git history with `csb restore`
93
+ - **Readable chat logs**: `csb distill` renders any session as an IM-style log -- the full JSONL stays preserved regardless
94
+ - **Two-commit model**: Noise (transient state) and user (configs, skills) committed separately
95
+ - **Unattended operation**: `--no-gpg-sign`, `--quiet`, lock file -- designed for cron and Task Scheduler
96
+ - **Cross-platform**: Works on Windows, Linux, macOS, BSD
97
+
98
+ ## Commands
99
+
100
+ The daily drivers:
101
+
102
+ ```bash
103
+ csb backup # Scan, index, git commit
104
+ csb list # Timeline of sessions (filter, sort, --deleted)
105
+ csb scan -d <path> --deleted # Find (and bulk-restore) what was purged in a folder
106
+ csb search "oauth callback" # Full-text search across every conversation
107
+ csb distill <query> # Read a session as a chat log -> ~/.claude/distilled/
108
+ csb resume <query> # Reopen in Claude Code (UUID, prefix, name, keyword...)
109
+ csb view <query> # Open in Claude Code History Viewer
110
+ csb restore <session-id> # Recover a deleted session from git history
111
+ csb status # Summary stats
112
+ ```
113
+
114
+ Every command, every flag, and the nitty-gritties live in **[docs/commands.md](docs/commands.md)**.
115
+
116
+ ### Common workflows
117
+
118
+ The patterns that come up every day:
119
+
120
+ ```bash
121
+ # "What sessions touched THIS project?" -- cd into any folder you were
122
+ # working in and ask. The shortcut form needs no flags to remember.
123
+ cd ~/code/my-project
124
+ csb scan .
125
+
126
+ # "What was I working on before the reboot / crash / weekend?"
127
+ csb list -n 5
128
+
129
+ # "I remember discussing it, but in WHICH session?" -- search by content,
130
+ # then read the winner like a chat log.
131
+ csb search "rate limiter backoff"
132
+ csb distill <uuid-from-the-hit>
133
+
134
+ # "Pick up exactly where I left off" -- by name, prefix, or keyword.
135
+ csb resume MY-PROJECT__2026-6-6__that-refactor
136
+
137
+ # "Claude Code purged something I needed."
138
+ csb list --deleted
139
+ csb restore MY-PROJECT__2025-5-25__redesign #or <session-id / id-fragment>
140
+ ```
141
+
142
+ ### Searching conversations
143
+
144
+ `csb search` finds old sessions by **what was discussed**, not just by folder or name -- sub-second across tens of thousands of messages via per-project FTS5 indexes (run `csb update build-fts5` once to build them).
145
+
146
+ ```bash
147
+ csb search "oauth callback" # literal substring, case-insensitive
148
+ csb search -E "refresh.*token" -C 3 # regex, with 3 events of context
149
+ ```
150
+
151
+ Full details (what's indexed, source channels, JSON output, freshness semantics): **[docs/commands.md](docs/commands.md#searching-conversations)**.
152
+
153
+ ### Reading conversations (distill)
154
+
155
+ `csb search` finds the needle; `csb distill` lets you read the haystack comfortably, with an instant-messenger-style log with timestamped speaker turns (`<User>`, `<Claude>`, `<Agent:explore>`) and one-line tool calls instead of walls of tool output. Markdown-friendly (Typora) and editor-friendly (Vim / VSCode / etc).
156
+
157
+ ```bash
158
+ csb distill <anything-that-identifies-a-session> # writes ~/.claude/distilled/<slug>/<uuid>.md
159
+ ```
160
+
161
+ The distilled file is a *reading layer* -- the full JSONL remains the preserved record. Filters, channels, and the `distill_policy` config: **[docs/commands.md](docs/commands.md#reading-conversations-distill)**.
162
+
163
+ ### Recovery
164
+
165
+ When Claude Code purges a session that you wanted to keep, csb recovers it from git history **byte+metadata-exact**. This includes the full footprint (transcript, subagents, tool-results, logger files), recreated symlinks, and original timestamps -- a recovered session is indistinguishable from one that was never deleted. `resume`/`view`/`distill` all offer the restore inline when they hit a pruned session.
166
+
167
+ ```bash
168
+ csb list --deleted # what's gone?
169
+ csb restore <session-id> # bring one back
170
+ csb scan -d <path> --deleted --restore --dry-run # preview a bulk recovery
171
+ ```
172
+
173
+ Single + bulk recovery, guarantees and limits, purge-TTL management: **[docs/commands.md](docs/commands.md#recovery)**. Maintenance verbs (`csb update *`): **[docs/maintenance.md](docs/maintenance.md)**.
174
+
175
+ ## How It Works
176
+
177
+ ```mermaid
178
+ flowchart LR
179
+ subgraph GitRepo["~/.claude/ (your git repo)"]
180
+ direction TB
181
+ Data["projects/*.jsonl<br>session-states/<br>file-history/"]
182
+ end
183
+
184
+ subgraph CSB["csb Tool"]
185
+ direction TB
186
+ Scripts["scanner.py<br>metadata.py<br><i>(extract names, dates, folders)</i>"]
187
+ Restore["restore.py"]
188
+ end
189
+
190
+ DB[("session-backup.db<br>(rebuildable metadata cache)")]
191
+
192
+ Data -- "scan & read" --> Scripts
193
+ Scripts -- "upsert" --> DB
194
+ Scripts -- "git add + commit" --> Data
195
+ Data -- "git show {commit}:path" --> Restore
196
+ ```
197
+
198
+ **Key principle**: Git is the source of truth. The SQLite database is a rebuildable index for fast queries. If the DB is lost or corrupted, `csb update rebuild-index` reconstructs it while preserving deleted-session metadata. See [`docs/maintenance.md`](docs/maintenance.md) for the `csb update` family of maintenance verbs.
199
+
200
+ ## Automation
201
+
202
+ The Claude Code plugin (from Quick Start above) covers most users: PreCompact fires before `/compact`, SessionEnd on exit. For manual hooks, cron, Task Scheduler, and distill-on-backup, see **[docs/automation.md](docs/automation.md)**.
203
+
204
+ ## Requirements
205
+
206
+ - **Python 3.10+**
207
+ - **Git** (for backup storage)
208
+ - **`~/.claude/`** initialized as a git repository (`git -C ~/.claude init`)
209
+ - Moved your Claude directory? csb follows `CLAUDE_CONFIG_DIR` automatically; `--claude-dir`, `CLAUDE_DIR`, and the `claude_dir` config key also work
210
+
211
+ ## Installation
212
+
213
+ ```bash
214
+ # From PyPI (recommended)
215
+ pip install claude-session-backup
216
+
217
+ # Latest unreleased build from GitHub
218
+ pip install git+https://github.com/DazzleML/Claude-Session-Backup.git
219
+
220
+ # From source (development / contributing)
221
+ git clone https://github.com/DazzleML/Claude-Session-Backup.git
222
+ cd Claude-Session-Backup
223
+ pip install -e ".[dev]"
224
+ ```
225
+
226
+ Full documentation index: **[docs/README.md](docs/README.md)**.
227
+
228
+ ## Contributing
229
+
230
+ Contributions welcome! Please open an issue or submit a pull request.
231
+
232
+ See **[CONTRIBUTING.md](CONTRIBUTING.md)** for:
233
+ - Development setup (`pip install -e ".[dev]"`)
234
+ - Running the test suite and human test checklists (`tests/checklists/`)
235
+ - Version management with `sync-versions.py`
236
+ - Pull request checklist
237
+
238
+ Like the project?
239
+
240
+ [!["Buy Me A Coffee"](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/djdarcy)
241
+
242
+ ## Related Projects
243
+
244
+ - [claude-session-logger](https://github.com/DazzleML/claude-session-logger) - Real-time per-session tool/conversation logging; csb backs up and restores its files, and its session naming + state-file conventions shaped csb's
245
+ - [dazzle-filekit](https://github.com/DazzleLib/dazzle-filekit) - Cross-platform file operations toolkit (symlink recreation, timestamp restore)
246
+
247
+ ## Acknowledgements
248
+
249
+ - [claude-vault](https://github.com/kuroko1t/claude-vault) by [@kuroko1t](https://github.com/kuroko1t) -- FTS5 search design, JSONL parsing patterns, Claude Code hook integration. Serendipitously started development on `csb` a week or so before [kuroko1t's blog post](https://dev.to/kuroko1t/i-built-a-tool-to-stop-losing-my-claude-code-conversation-history-5500) laying out the problem.
250
+ - [claude-code-history-viewer](https://github.com/jhlee0409/claude-code-history-viewer) by [@jhlee0409](https://github.com/jhlee0409) - GUI session reader that `csb view` launches.
251
+
252
+ ## License
253
+
254
+ Claude-Session-Backup, Copyright (C) 2026 Dustin Darcy
255
+
256
+ Licensed under the [GNU General Public License v3.0](https://www.gnu.org/licenses/gpl-3.0.html) (GPL-3.0) -- see [LICENSE](LICENSE)
@@ -0,0 +1,224 @@
1
+ # Claude-Session-Backup
2
+
3
+ [![PyPI](https://img.shields.io/pypi/v/claude-session-backup?color=green)](https://pypi.org/project/claude-session-backup/)
4
+ [![Release Date](https://img.shields.io/github/release-date/DazzleML/Claude-Session-Backup?color=green)](https://github.com/DazzleML/Claude-Session-Backup/releases)
5
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
6
+ [![License: GPL v3](https://img.shields.io/badge/license-GPL%20v3-green.svg)](https://www.gnu.org/licenses/gpl-3.0.html)
7
+ [![Installs](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/djdarcy/7aa669e4d85856079eacc71f88c58f6b/raw/installs.json)](https://dazzleml.github.io/Claude-Session-Backup/stats/#installs)
8
+ [![Platform](https://img.shields.io/badge/platform-Windows%20%7C%20Linux%20%7C%20macOS%20%7C%20BSD-lightgrey.svg)](docs/platforms.md)
9
+
10
+ **Git-backed Claude Code session backup with timeline view, folder analysis, deletion detection, and session restore.**
11
+
12
+ ## The Problem
13
+
14
+ Claude Code stores session data in `~/.claude/projects/` as JSONL files. These can be silently deleted during upgrades, lossy-compacted via `/compact`, or lost when session compatibility breaks between versions. Once gone, your conversation history -- including debugging sessions, architectural decisions, and code review context -- is unrecoverable.
15
+
16
+ **csb** preserves every session in your existing `~/.claude` git repository, builds a searchable metadata index, detects deletions, and can restore lost sessions from git history.
17
+
18
+ > [!NOTE]
19
+ > **Alpha software (cusp of beta) -- nearly feature-complete.** Everything on the original roadmap has shipped: backup, deletion detection, content search (JSONL+sesslogs+FTS5), full session restore (a deleted session's complete footprint recovered from git -- transcript, subagents, tool-results, logger sesslogs -- with original timestamps and symlinks, resumable in Claude Code), a viewer launcher (`csb view`), and a human-readable chat-log layer (`csb distill`).
20
+ >
21
+ > Staying alpha a little longer while the tires get kicked; beta follows once v0.4.0 has settled. Expect occasional rough edges and breaking changes between minor versions until then. By all means use it (and please [file issues](https://github.com/DazzleML/Claude-Session-Backup/issues)) but, as with any backup tool, keep a second copy of anything irreplaceable.
22
+
23
+ ## Quick Start
24
+
25
+ Three commands install everything -- the CLI plus the Claude Code plugin that fires backups automatically on PreCompact and SessionEnd:
26
+
27
+ ```bash
28
+ # 1. Install the csb CLI
29
+ pip install claude-session-backup
30
+
31
+ # 2. Add the DazzleML marketplace (one-time)
32
+ claude plugin marketplace add "DazzleML/Claude-Session-Backup"
33
+
34
+ # 3. Install the plugin -- registers the PreCompact + SessionEnd hooks
35
+ claude plugin install claude-session-backup@dazzle-claude-session-backup
36
+ ```
37
+
38
+ Then verify it works:
39
+
40
+ ```bash
41
+ # Build the index from existing sessions (no git commits yet)
42
+ csb backup --no-commit
43
+
44
+ # See your session timeline
45
+ csb list
46
+
47
+ # Full backup with git commits (separate noise + user commits, unsigned)
48
+ csb backup
49
+ ```
50
+
51
+ > [!TIP]
52
+ > **Pair with [claude-session-logger](https://github.com/DazzleML/claude-session-logger/)** for full searchable history. csb preserves Claude Code's session transcripts (`projects/<slug>/<uuid>.jsonl`). The logger captures the *richer* per-session data alongside them -- tool calls, shell commands, agent dispatches -- written to `~/.claude/sesslogs/`. csb backs up those logger files too (it backs up everything under `~/.claude/` via the noise commits), and `csb restore` brings the whole footprint back together: transcript + subagents + tool-results + logger state + sesslogs. The two projects are independent (csb works fine without the logger) but they're designed to complement each other.
53
+
54
+ ## Features
55
+
56
+ - **Full session preservation**: Every byte of JSONL, subagent data, tool results backed up via git
57
+ - **Timeline view**: Sessions sorted by last use with relative dates, start folder, and top N working directories
58
+ - **Folder analysis**: See where work actually happened -- the most-used folder is highlighted
59
+ - **Deletion detection**: Know when Claude Code removes a session you previously tracked
60
+ - **Session restore**: Recover deleted sessions from git history with `csb restore`
61
+ - **Readable chat logs**: `csb distill` renders any session as an IM-style log -- the full JSONL stays preserved regardless
62
+ - **Two-commit model**: Noise (transient state) and user (configs, skills) committed separately
63
+ - **Unattended operation**: `--no-gpg-sign`, `--quiet`, lock file -- designed for cron and Task Scheduler
64
+ - **Cross-platform**: Works on Windows, Linux, macOS, BSD
65
+
66
+ ## Commands
67
+
68
+ The daily drivers:
69
+
70
+ ```bash
71
+ csb backup # Scan, index, git commit
72
+ csb list # Timeline of sessions (filter, sort, --deleted)
73
+ csb scan -d <path> --deleted # Find (and bulk-restore) what was purged in a folder
74
+ csb search "oauth callback" # Full-text search across every conversation
75
+ csb distill <query> # Read a session as a chat log -> ~/.claude/distilled/
76
+ csb resume <query> # Reopen in Claude Code (UUID, prefix, name, keyword...)
77
+ csb view <query> # Open in Claude Code History Viewer
78
+ csb restore <session-id> # Recover a deleted session from git history
79
+ csb status # Summary stats
80
+ ```
81
+
82
+ Every command, every flag, and the nitty-gritties live in **[docs/commands.md](docs/commands.md)**.
83
+
84
+ ### Common workflows
85
+
86
+ The patterns that come up every day:
87
+
88
+ ```bash
89
+ # "What sessions touched THIS project?" -- cd into any folder you were
90
+ # working in and ask. The shortcut form needs no flags to remember.
91
+ cd ~/code/my-project
92
+ csb scan .
93
+
94
+ # "What was I working on before the reboot / crash / weekend?"
95
+ csb list -n 5
96
+
97
+ # "I remember discussing it, but in WHICH session?" -- search by content,
98
+ # then read the winner like a chat log.
99
+ csb search "rate limiter backoff"
100
+ csb distill <uuid-from-the-hit>
101
+
102
+ # "Pick up exactly where I left off" -- by name, prefix, or keyword.
103
+ csb resume MY-PROJECT__2026-6-6__that-refactor
104
+
105
+ # "Claude Code purged something I needed."
106
+ csb list --deleted
107
+ csb restore MY-PROJECT__2025-5-25__redesign #or <session-id / id-fragment>
108
+ ```
109
+
110
+ ### Searching conversations
111
+
112
+ `csb search` finds old sessions by **what was discussed**, not just by folder or name -- sub-second across tens of thousands of messages via per-project FTS5 indexes (run `csb update build-fts5` once to build them).
113
+
114
+ ```bash
115
+ csb search "oauth callback" # literal substring, case-insensitive
116
+ csb search -E "refresh.*token" -C 3 # regex, with 3 events of context
117
+ ```
118
+
119
+ Full details (what's indexed, source channels, JSON output, freshness semantics): **[docs/commands.md](docs/commands.md#searching-conversations)**.
120
+
121
+ ### Reading conversations (distill)
122
+
123
+ `csb search` finds the needle; `csb distill` lets you read the haystack comfortably, with an instant-messenger-style log with timestamped speaker turns (`<User>`, `<Claude>`, `<Agent:explore>`) and one-line tool calls instead of walls of tool output. Markdown-friendly (Typora) and editor-friendly (Vim / VSCode / etc).
124
+
125
+ ```bash
126
+ csb distill <anything-that-identifies-a-session> # writes ~/.claude/distilled/<slug>/<uuid>.md
127
+ ```
128
+
129
+ The distilled file is a *reading layer* -- the full JSONL remains the preserved record. Filters, channels, and the `distill_policy` config: **[docs/commands.md](docs/commands.md#reading-conversations-distill)**.
130
+
131
+ ### Recovery
132
+
133
+ When Claude Code purges a session that you wanted to keep, csb recovers it from git history **byte+metadata-exact**. This includes the full footprint (transcript, subagents, tool-results, logger files), recreated symlinks, and original timestamps -- a recovered session is indistinguishable from one that was never deleted. `resume`/`view`/`distill` all offer the restore inline when they hit a pruned session.
134
+
135
+ ```bash
136
+ csb list --deleted # what's gone?
137
+ csb restore <session-id> # bring one back
138
+ csb scan -d <path> --deleted --restore --dry-run # preview a bulk recovery
139
+ ```
140
+
141
+ Single + bulk recovery, guarantees and limits, purge-TTL management: **[docs/commands.md](docs/commands.md#recovery)**. Maintenance verbs (`csb update *`): **[docs/maintenance.md](docs/maintenance.md)**.
142
+
143
+ ## How It Works
144
+
145
+ ```mermaid
146
+ flowchart LR
147
+ subgraph GitRepo["~/.claude/ (your git repo)"]
148
+ direction TB
149
+ Data["projects/*.jsonl<br>session-states/<br>file-history/"]
150
+ end
151
+
152
+ subgraph CSB["csb Tool"]
153
+ direction TB
154
+ Scripts["scanner.py<br>metadata.py<br><i>(extract names, dates, folders)</i>"]
155
+ Restore["restore.py"]
156
+ end
157
+
158
+ DB[("session-backup.db<br>(rebuildable metadata cache)")]
159
+
160
+ Data -- "scan & read" --> Scripts
161
+ Scripts -- "upsert" --> DB
162
+ Scripts -- "git add + commit" --> Data
163
+ Data -- "git show {commit}:path" --> Restore
164
+ ```
165
+
166
+ **Key principle**: Git is the source of truth. The SQLite database is a rebuildable index for fast queries. If the DB is lost or corrupted, `csb update rebuild-index` reconstructs it while preserving deleted-session metadata. See [`docs/maintenance.md`](docs/maintenance.md) for the `csb update` family of maintenance verbs.
167
+
168
+ ## Automation
169
+
170
+ The Claude Code plugin (from Quick Start above) covers most users: PreCompact fires before `/compact`, SessionEnd on exit. For manual hooks, cron, Task Scheduler, and distill-on-backup, see **[docs/automation.md](docs/automation.md)**.
171
+
172
+ ## Requirements
173
+
174
+ - **Python 3.10+**
175
+ - **Git** (for backup storage)
176
+ - **`~/.claude/`** initialized as a git repository (`git -C ~/.claude init`)
177
+ - Moved your Claude directory? csb follows `CLAUDE_CONFIG_DIR` automatically; `--claude-dir`, `CLAUDE_DIR`, and the `claude_dir` config key also work
178
+
179
+ ## Installation
180
+
181
+ ```bash
182
+ # From PyPI (recommended)
183
+ pip install claude-session-backup
184
+
185
+ # Latest unreleased build from GitHub
186
+ pip install git+https://github.com/DazzleML/Claude-Session-Backup.git
187
+
188
+ # From source (development / contributing)
189
+ git clone https://github.com/DazzleML/Claude-Session-Backup.git
190
+ cd Claude-Session-Backup
191
+ pip install -e ".[dev]"
192
+ ```
193
+
194
+ Full documentation index: **[docs/README.md](docs/README.md)**.
195
+
196
+ ## Contributing
197
+
198
+ Contributions welcome! Please open an issue or submit a pull request.
199
+
200
+ See **[CONTRIBUTING.md](CONTRIBUTING.md)** for:
201
+ - Development setup (`pip install -e ".[dev]"`)
202
+ - Running the test suite and human test checklists (`tests/checklists/`)
203
+ - Version management with `sync-versions.py`
204
+ - Pull request checklist
205
+
206
+ Like the project?
207
+
208
+ [!["Buy Me A Coffee"](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/djdarcy)
209
+
210
+ ## Related Projects
211
+
212
+ - [claude-session-logger](https://github.com/DazzleML/claude-session-logger) - Real-time per-session tool/conversation logging; csb backs up and restores its files, and its session naming + state-file conventions shaped csb's
213
+ - [dazzle-filekit](https://github.com/DazzleLib/dazzle-filekit) - Cross-platform file operations toolkit (symlink recreation, timestamp restore)
214
+
215
+ ## Acknowledgements
216
+
217
+ - [claude-vault](https://github.com/kuroko1t/claude-vault) by [@kuroko1t](https://github.com/kuroko1t) -- FTS5 search design, JSONL parsing patterns, Claude Code hook integration. Serendipitously started development on `csb` a week or so before [kuroko1t's blog post](https://dev.to/kuroko1t/i-built-a-tool-to-stop-losing-my-claude-code-conversation-history-5500) laying out the problem.
218
+ - [claude-code-history-viewer](https://github.com/jhlee0409/claude-code-history-viewer) by [@jhlee0409](https://github.com/jhlee0409) - GUI session reader that `csb view` launches.
219
+
220
+ ## License
221
+
222
+ Claude-Session-Backup, Copyright (C) 2026 Dustin Darcy
223
+
224
+ Licensed under the [GNU General Public License v3.0](https://www.gnu.org/licenses/gpl-3.0.html) (GPL-3.0) -- see [LICENSE](LICENSE)
@@ -15,13 +15,13 @@ To bump version: python scripts/sync-versions.py --bump patch
15
15
  # Version components - edit these for version bumps
16
16
  MAJOR = 0
17
17
  MINOR = 4
18
- PATCH = 0
18
+ PATCH = 2
19
19
  PHASE = "" # Per-MINOR feature set: None, "alpha", "beta", "rc1", etc.
20
20
  PRE_RELEASE_NUM = 1 # PEP 440 pre-release number (e.g., a1, b2)
21
21
  PROJECT_PHASE = "alpha" # Project-wide: "prealpha", "alpha", "beta", "stable"
22
22
 
23
23
  # Auto-updated by git hooks - do not edit manually
24
- __version__ = "0.4.0"
24
+ __version__ = "0.4.2"
25
25
  __app_name__ = "claude-session-backup"
26
26
 
27
27
 
@@ -744,41 +744,37 @@ def cmd_restore(args) -> int:
744
744
  conn = open_db(config["index_path"])
745
745
  init_schema(conn)
746
746
 
747
- # Resolve the input against the DB. Three outcomes matter here:
748
- # - Success: use the DB row's jsonl_path.
749
- # - No match + input is a full UUID: fall through to git-history
750
- # fallback (#28). Don't exit -- the DB may be missing the row
751
- # (post-rebuild-index, fresh clone, etc.) while git still has the
752
- # JSONL.
753
- # - No match + input is a prefix/suffix: exit 1. Prefix matching
754
- # requires a DB row to disambiguate; without one we can't help.
755
- # - Ambiguous / invalid input: propagate the resolver's exit code.
756
- from .ids import (
757
- AmbiguousSessionID,
758
- InvalidSessionIDInput,
759
- NoSuchSessionID,
760
- format_ambiguous_error,
761
- resolve_session_id,
747
+ # Resolve the input (#44 -- restore was the last command without the
748
+ # shared multi-modal resolver). Order:
749
+ # 1. Strict ID resolver (prefix/suffix; ambiguity still exits 2).
750
+ # A plain miss falls through silently (miss_ok) -- the input may
751
+ # be a session NAME, path, folder, or keyword.
752
+ # 2. Shared multi-modal resolver (same surface as resume/view/
753
+ # distill). Multi-match -> candidates timeline, exit 1.
754
+ # 3. Still nothing + input is a full UUID: git-history fallback
755
+ # (#28) below -- the DB may be missing the row while git still
756
+ # has the JSONL. Names can't use the fallback (filenames carry
757
+ # UUIDs, not titles).
758
+ full_id, exit_code = _resolve_session_or_exit(
759
+ conn, args.session_id, miss_ok=True
762
760
  )
763
- full_id: str | None = None
764
- session = None
765
- try:
766
- full_id = resolve_session_id(conn, args.session_id)
767
- session = get_session(conn, full_id)
768
- except AmbiguousSessionID as e:
769
- print(format_ambiguous_error(e), file=sys.stderr)
770
- conn.close()
771
- return 2
772
- except InvalidSessionIDInput as e:
773
- print(f"Error: {e}", file=sys.stderr)
774
- conn.close()
775
- return 2
776
- except NoSuchSessionID:
777
- # Don't print yet -- maybe git history can save us if the input is
778
- # a full UUID.
779
- pass
780
- finally:
761
+ if full_id is None and exit_code:
781
762
  conn.close()
763
+ return exit_code
764
+ session = get_session(conn, full_id) if full_id else None
765
+ if session is None:
766
+ result, method = _resolve_session_query(
767
+ args.session_id, conn, claude_dir
768
+ )
769
+ if isinstance(result, list):
770
+ label = method.split(":", 1)[1] if ":" in method else method
771
+ _show_view_candidates(result, args.session_id, label)
772
+ conn.close()
773
+ return 1
774
+ if result is not None:
775
+ session = result
776
+ full_id = session["session_id"]
777
+ conn.close()
782
778
 
783
779
  # Resolve jsonl_path via DB row if present, otherwise via git history.
784
780
  jsonl_path: str | None = None
@@ -44,14 +44,39 @@ DEFAULT_CONFIG = {
44
44
 
45
45
  # Environment variable overrides (CLI flag > env var > config file > default)
46
46
  ENV_CLAUDE_DIR = "CLAUDE_DIR"
47
+ # Claude Code's OWN relocation variable (#45). Honoring it means csb
48
+ # automatically follows setups that moved the data directory the way
49
+ # Claude Code itself supports (multi-workspace containers, host-mounted
50
+ # dirs, worktree-isolated agent environments) -- zero csb configuration.
51
+ # csb's CLAUDE_DIR wins when both are set (the more specific override).
52
+ ENV_CLAUDE_CONFIG_DIR = "CLAUDE_CONFIG_DIR"
47
53
  ENV_DB_PATH = "CLAUDE_SESSION_BACKUP_DB"
48
54
 
49
55
  CONFIG_FILENAME = "session-backup-config.json"
50
56
 
51
57
 
58
+ def _env_claude_dir():
59
+ """The effective env-var override for the Claude data directory:
60
+ csb's own CLAUDE_DIR first, then Claude Code's CLAUDE_CONFIG_DIR.
61
+ None when neither is set."""
62
+ return os.environ.get(ENV_CLAUDE_DIR) or os.environ.get(ENV_CLAUDE_CONFIG_DIR)
63
+
64
+
65
+ def _default_claude_dir() -> Path:
66
+ """The default Claude directory when no explicit path is given:
67
+ env override (CLAUDE_DIR / CLAUDE_CONFIG_DIR) or ~/.claude.
68
+
69
+ The SINGLE place this default lives (#45) -- every fallback site
70
+ routes through here so relocated setups are followed everywhere,
71
+ including the chicken-and-egg reads of csb's own config file and
72
+ Claude Code's settings.json."""
73
+ env = _env_claude_dir()
74
+ return Path(env).expanduser() if env else Path.home() / ".claude"
75
+
76
+
52
77
  def get_config_path(claude_dir=None):
53
78
  """Return the config file path."""
54
- base = Path(claude_dir).expanduser() if claude_dir else Path.home() / ".claude"
79
+ base = Path(claude_dir).expanduser() if claude_dir else _default_claude_dir()
55
80
  return base / CONFIG_FILENAME
56
81
 
57
82
 
@@ -64,7 +89,7 @@ def get_settings_path(claude_dir=None):
64
89
  purge countdown and (via the ``settings:`` namespace in ``csb config``)
65
90
  can edit the TTL through it.
66
91
  """
67
- base = Path(claude_dir).expanduser() if claude_dir else Path.home() / ".claude"
92
+ base = Path(claude_dir).expanduser() if claude_dir else _default_claude_dir()
68
93
  return base / "settings.json"
69
94
 
70
95
 
@@ -77,7 +102,7 @@ def load_config(claude_dir=None):
77
102
  config = dict(DEFAULT_CONFIG)
78
103
 
79
104
  # Apply env var overrides before config file (config file can still override)
80
- env_claude_dir = os.environ.get(ENV_CLAUDE_DIR)
105
+ env_claude_dir = _env_claude_dir() # CLAUDE_DIR, else CLAUDE_CONFIG_DIR (#45)
81
106
  env_db = os.environ.get(ENV_DB_PATH)
82
107
  if env_claude_dir:
83
108
  config["claude_dir"] = env_claude_dir
@@ -103,6 +128,16 @@ def load_config(claude_dir=None):
103
128
  if env_db:
104
129
  config["index_path"] = env_db
105
130
 
131
+ # The DB lives INSIDE the claude_dir unless explicitly overridden
132
+ # (#45): when index_path is still the stock default but claude_dir
133
+ # was relocated (flag / CLAUDE_DIR / CLAUDE_CONFIG_DIR / config),
134
+ # follow the relocation -- otherwise sessions would scan from the
135
+ # new dir while the index silently pinned to ~/.claude.
136
+ if config["index_path"] == DEFAULT_CONFIG["index_path"]:
137
+ config["index_path"] = str(
138
+ Path(config["claude_dir"]).expanduser() / "session-backup.db"
139
+ )
140
+
106
141
  return config
107
142
 
108
143