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.
- claude_session_backup-0.4.2/PKG-INFO +256 -0
- claude_session_backup-0.4.2/README.md +224 -0
- {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/claude_session_backup/_version.py +2 -2
- {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/claude_session_backup/commands.py +29 -33
- {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/claude_session_backup/config.py +38 -3
- claude_session_backup-0.4.2/claude_session_backup.egg-info/PKG-INFO +256 -0
- {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/tests/test_config.py +64 -0
- {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/tests/test_restore.py +81 -0
- claude_session_backup-0.4.0/PKG-INFO +0 -356
- claude_session_backup-0.4.0/README.md +0 -324
- claude_session_backup-0.4.0/claude_session_backup.egg-info/PKG-INFO +0 -356
- {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/LICENSE +0 -0
- {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/claude_session_backup/__init__.py +0 -0
- {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/claude_session_backup/__main__.py +0 -0
- {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/claude_session_backup/cli.py +0 -0
- {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/claude_session_backup/distill.py +0 -0
- {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/claude_session_backup/fts5_db.py +0 -0
- {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/claude_session_backup/fts5_importer.py +0 -0
- {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/claude_session_backup/fts5_index.py +0 -0
- {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/claude_session_backup/fts5_migrations.py +0 -0
- {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/claude_session_backup/fts_paths.py +0 -0
- {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/claude_session_backup/git_ops.py +0 -0
- {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/claude_session_backup/ids.py +0 -0
- {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/claude_session_backup/index.py +0 -0
- {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/claude_session_backup/lockfile.py +0 -0
- {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/claude_session_backup/metadata.py +0 -0
- {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/claude_session_backup/migrations.py +0 -0
- {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/claude_session_backup/pathkit.py +0 -0
- {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/claude_session_backup/scanner.py +0 -0
- {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/claude_session_backup/search.py +0 -0
- {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/claude_session_backup/search_render.py +0 -0
- {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/claude_session_backup/sesslog_parser.py +0 -0
- {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/claude_session_backup/sesslog_scanner.py +0 -0
- {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/claude_session_backup/timeline.py +0 -0
- {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/claude_session_backup/transcript_walker.py +0 -0
- {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/claude_session_backup.egg-info/SOURCES.txt +0 -0
- {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/claude_session_backup.egg-info/dependency_links.txt +0 -0
- {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/claude_session_backup.egg-info/entry_points.txt +0 -0
- {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/claude_session_backup.egg-info/requires.txt +0 -0
- {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/claude_session_backup.egg-info/top_level.txt +0 -0
- {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/pyproject.toml +0 -0
- {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/setup.cfg +0 -0
- {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/tests/test_backup_hook.py +0 -0
- {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/tests/test_cli.py +0 -0
- {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/tests/test_commands.py +0 -0
- {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/tests/test_distill.py +0 -0
- {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/tests/test_fts5_db.py +0 -0
- {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/tests/test_fts5_importer.py +0 -0
- {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/tests/test_fts5_index.py +0 -0
- {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/tests/test_fts5_migrations.py +0 -0
- {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/tests/test_fts_paths.py +0 -0
- {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/tests/test_hook.py +0 -0
- {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/tests/test_ids.py +0 -0
- {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/tests/test_index.py +0 -0
- {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/tests/test_lockfile.py +0 -0
- {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/tests/test_metadata.py +0 -0
- {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/tests/test_migrations.py +0 -0
- {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/tests/test_pathkit.py +0 -0
- {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/tests/test_scanner.py +0 -0
- {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/tests/test_search.py +0 -0
- {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/tests/test_search_render.py +0 -0
- {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/tests/test_session_sources.py +0 -0
- {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/tests/test_sesslog_parser.py +0 -0
- {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/tests/test_sesslog_scanner.py +0 -0
- {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/tests/test_timeline.py +0 -0
- {claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/tests/test_version.py +0 -0
- {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
|
+
[](https://pypi.org/project/claude-session-backup/)
|
|
36
|
+
[](https://github.com/DazzleML/Claude-Session-Backup/releases)
|
|
37
|
+
[](https://www.python.org/downloads/)
|
|
38
|
+
[](https://www.gnu.org/licenses/gpl-3.0.html)
|
|
39
|
+
[](https://dazzleml.github.io/Claude-Session-Backup/stats/#installs)
|
|
40
|
+
[](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
|
+
[](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
|
+
[](https://pypi.org/project/claude-session-backup/)
|
|
4
|
+
[](https://github.com/DazzleML/Claude-Session-Backup/releases)
|
|
5
|
+
[](https://www.python.org/downloads/)
|
|
6
|
+
[](https://www.gnu.org/licenses/gpl-3.0.html)
|
|
7
|
+
[](https://dazzleml.github.io/Claude-Session-Backup/stats/#installs)
|
|
8
|
+
[](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
|
+
[](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)
|
{claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/claude_session_backup/_version.py
RENAMED
|
@@ -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 =
|
|
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.
|
|
24
|
+
__version__ = "0.4.2"
|
|
25
25
|
__app_name__ = "claude-session-backup"
|
|
26
26
|
|
|
27
27
|
|
{claude_session_backup-0.4.0 → claude_session_backup-0.4.2}/claude_session_backup/commands.py
RENAMED
|
@@ -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
|
|
748
|
-
#
|
|
749
|
-
#
|
|
750
|
-
#
|
|
751
|
-
#
|
|
752
|
-
#
|
|
753
|
-
#
|
|
754
|
-
#
|
|
755
|
-
#
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|