commuter 0.1.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.
- commuter-0.1.0/.claude/settings.local.json +13 -0
- commuter-0.1.0/.gitignore +207 -0
- commuter-0.1.0/LICENSE +21 -0
- commuter-0.1.0/PKG-INFO +9 -0
- commuter-0.1.0/README.md +0 -0
- commuter-0.1.0/commuter-requirements.md +409 -0
- commuter-0.1.0/pyproject.toml +24 -0
- commuter-0.1.0/src/commuter/__init__.py +1 -0
- commuter-0.1.0/src/commuter/backends/__init__.py +54 -0
- commuter-0.1.0/src/commuter/backends/claude_code.py +396 -0
- commuter-0.1.0/src/commuter/bundle.py +100 -0
- commuter-0.1.0/src/commuter/cli.py +507 -0
- commuter-0.1.0/src/commuter/config.py +48 -0
- commuter-0.1.0/src/commuter/git_utils.py +62 -0
- commuter-0.1.0/src/commuter/lineage.py +45 -0
- commuter-0.1.0/src/commuter/pathmap.py +37 -0
- commuter-0.1.0/tests/__init__.py +0 -0
- commuter-0.1.0/tests/fixtures/sample_session.jsonl +5 -0
- commuter-0.1.0/tests/test_bundle.py +125 -0
- commuter-0.1.0/tests/test_claude_code.py +200 -0
- commuter-0.1.0/tests/test_lineage.py +70 -0
- commuter-0.1.0/tests/test_pathmap.py +58 -0
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[codz]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
# Usually these files are written by a python script from a template
|
|
31
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
32
|
+
*.manifest
|
|
33
|
+
*.spec
|
|
34
|
+
|
|
35
|
+
# Installer logs
|
|
36
|
+
pip-log.txt
|
|
37
|
+
pip-delete-this-directory.txt
|
|
38
|
+
|
|
39
|
+
# Unit test / coverage reports
|
|
40
|
+
htmlcov/
|
|
41
|
+
.tox/
|
|
42
|
+
.nox/
|
|
43
|
+
.coverage
|
|
44
|
+
.coverage.*
|
|
45
|
+
.cache
|
|
46
|
+
nosetests.xml
|
|
47
|
+
coverage.xml
|
|
48
|
+
*.cover
|
|
49
|
+
*.py.cover
|
|
50
|
+
.hypothesis/
|
|
51
|
+
.pytest_cache/
|
|
52
|
+
cover/
|
|
53
|
+
|
|
54
|
+
# Translations
|
|
55
|
+
*.mo
|
|
56
|
+
*.pot
|
|
57
|
+
|
|
58
|
+
# Django stuff:
|
|
59
|
+
*.log
|
|
60
|
+
local_settings.py
|
|
61
|
+
db.sqlite3
|
|
62
|
+
db.sqlite3-journal
|
|
63
|
+
|
|
64
|
+
# Flask stuff:
|
|
65
|
+
instance/
|
|
66
|
+
.webassets-cache
|
|
67
|
+
|
|
68
|
+
# Scrapy stuff:
|
|
69
|
+
.scrapy
|
|
70
|
+
|
|
71
|
+
# Sphinx documentation
|
|
72
|
+
docs/_build/
|
|
73
|
+
|
|
74
|
+
# PyBuilder
|
|
75
|
+
.pybuilder/
|
|
76
|
+
target/
|
|
77
|
+
|
|
78
|
+
# Jupyter Notebook
|
|
79
|
+
.ipynb_checkpoints
|
|
80
|
+
|
|
81
|
+
# IPython
|
|
82
|
+
profile_default/
|
|
83
|
+
ipython_config.py
|
|
84
|
+
|
|
85
|
+
# pyenv
|
|
86
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
87
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
88
|
+
# .python-version
|
|
89
|
+
|
|
90
|
+
# pipenv
|
|
91
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
92
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
93
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
94
|
+
# install all needed dependencies.
|
|
95
|
+
#Pipfile.lock
|
|
96
|
+
|
|
97
|
+
# UV
|
|
98
|
+
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
|
99
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
100
|
+
# commonly ignored for libraries.
|
|
101
|
+
#uv.lock
|
|
102
|
+
|
|
103
|
+
# poetry
|
|
104
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
105
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
106
|
+
# commonly ignored for libraries.
|
|
107
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
108
|
+
#poetry.lock
|
|
109
|
+
#poetry.toml
|
|
110
|
+
|
|
111
|
+
# pdm
|
|
112
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
113
|
+
# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
|
|
114
|
+
# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
|
|
115
|
+
#pdm.lock
|
|
116
|
+
#pdm.toml
|
|
117
|
+
.pdm-python
|
|
118
|
+
.pdm-build/
|
|
119
|
+
|
|
120
|
+
# pixi
|
|
121
|
+
# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
|
|
122
|
+
#pixi.lock
|
|
123
|
+
# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
|
|
124
|
+
# in the .venv directory. It is recommended not to include this directory in version control.
|
|
125
|
+
.pixi
|
|
126
|
+
|
|
127
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
128
|
+
__pypackages__/
|
|
129
|
+
|
|
130
|
+
# Celery stuff
|
|
131
|
+
celerybeat-schedule
|
|
132
|
+
celerybeat.pid
|
|
133
|
+
|
|
134
|
+
# SageMath parsed files
|
|
135
|
+
*.sage.py
|
|
136
|
+
|
|
137
|
+
# Environments
|
|
138
|
+
.env
|
|
139
|
+
.envrc
|
|
140
|
+
.venv
|
|
141
|
+
env/
|
|
142
|
+
venv/
|
|
143
|
+
ENV/
|
|
144
|
+
env.bak/
|
|
145
|
+
venv.bak/
|
|
146
|
+
|
|
147
|
+
# Spyder project settings
|
|
148
|
+
.spyderproject
|
|
149
|
+
.spyproject
|
|
150
|
+
|
|
151
|
+
# Rope project settings
|
|
152
|
+
.ropeproject
|
|
153
|
+
|
|
154
|
+
# mkdocs documentation
|
|
155
|
+
/site
|
|
156
|
+
|
|
157
|
+
# mypy
|
|
158
|
+
.mypy_cache/
|
|
159
|
+
.dmypy.json
|
|
160
|
+
dmypy.json
|
|
161
|
+
|
|
162
|
+
# Pyre type checker
|
|
163
|
+
.pyre/
|
|
164
|
+
|
|
165
|
+
# pytype static type analyzer
|
|
166
|
+
.pytype/
|
|
167
|
+
|
|
168
|
+
# Cython debug symbols
|
|
169
|
+
cython_debug/
|
|
170
|
+
|
|
171
|
+
# PyCharm
|
|
172
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
173
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
174
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
175
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
176
|
+
#.idea/
|
|
177
|
+
|
|
178
|
+
# Abstra
|
|
179
|
+
# Abstra is an AI-powered process automation framework.
|
|
180
|
+
# Ignore directories containing user credentials, local state, and settings.
|
|
181
|
+
# Learn more at https://abstra.io/docs
|
|
182
|
+
.abstra/
|
|
183
|
+
|
|
184
|
+
# Visual Studio Code
|
|
185
|
+
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
|
|
186
|
+
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
|
|
187
|
+
# and can be added to the global gitignore or merged into this file. However, if you prefer,
|
|
188
|
+
# you could uncomment the following to ignore the entire vscode folder
|
|
189
|
+
# .vscode/
|
|
190
|
+
|
|
191
|
+
# Ruff stuff:
|
|
192
|
+
.ruff_cache/
|
|
193
|
+
|
|
194
|
+
# PyPI configuration file
|
|
195
|
+
.pypirc
|
|
196
|
+
|
|
197
|
+
# Cursor
|
|
198
|
+
# Cursor is an AI-powered code editor. `.cursorignore` specifies files/directories to
|
|
199
|
+
# exclude from AI features like autocomplete and code analysis. Recommended for sensitive data
|
|
200
|
+
# refer to https://docs.cursor.com/context/ignore-files
|
|
201
|
+
.cursorignore
|
|
202
|
+
.cursorindexingignore
|
|
203
|
+
|
|
204
|
+
# Marimo
|
|
205
|
+
marimo/_static/
|
|
206
|
+
marimo/_lsp/
|
|
207
|
+
__marimo__/
|
commuter-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ljbuturovic
|
|
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.
|
commuter-0.1.0/PKG-INFO
ADDED
commuter-0.1.0/README.md
ADDED
|
File without changes
|
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
# commuter: Portable AI Coding Session Transfer
|
|
2
|
+
|
|
3
|
+
## Problem
|
|
4
|
+
|
|
5
|
+
Claude Code's Remote Control feature lets you view a session from another device, but the session always runs on the original host machine. If that machine sleeps, loses network, or you want to work with local files on a different machine, you're stuck.
|
|
6
|
+
|
|
7
|
+
**Goal:** Enable true session migration between machines that share a synced filesystem (via Git, Dropbox, Syncthing, etc.). Export a coding session on Machine A, import it on Machine B, and continue working with full local file access on Machine B.
|
|
8
|
+
|
|
9
|
+
The initial implementation targets Claude Code. The architecture should be tool-agnostic so that future backends can support Codex CLI, Gemini CLI, and other AI coding tools.
|
|
10
|
+
|
|
11
|
+
## User Experience Walkthrough
|
|
12
|
+
|
|
13
|
+
### First-time setup (once per machine pair)
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# Install on BOTH machines
|
|
17
|
+
pip install commuter
|
|
18
|
+
|
|
19
|
+
# On each machine, tell it where your projects live
|
|
20
|
+
# Machine A (home desktop, Linux):
|
|
21
|
+
commuter config set path-map "/home/ljubomir/projects" "/Users/ljubomir/projects"
|
|
22
|
+
|
|
23
|
+
# Machine B (work laptop, macOS): same command
|
|
24
|
+
commuter config set path-map "/home/ljubomir/projects" "/Users/ljubomir/projects"
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
That's it for setup. The path map tells the tool how to translate paths between your machines. If your paths are identical on both machines, you can skip this step entirely.
|
|
28
|
+
|
|
29
|
+
### Daily workflow: Morning at home
|
|
30
|
+
|
|
31
|
+
You start working on your project at home:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
cd ~/projects/trivertiy-ml
|
|
35
|
+
claude
|
|
36
|
+
|
|
37
|
+
# ... work with Claude Code for an hour, build a new feature,
|
|
38
|
+
# debug a test failure, get deep into a conversation ...
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Time to leave for work. You want to continue this session on your office laptop.
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
# See what sessions are available
|
|
45
|
+
commuter list
|
|
46
|
+
|
|
47
|
+
ID PROJECT LAST ACTIVE SUMMARY
|
|
48
|
+
a1b2c3d ~/projects/trivertiy-ml 2 min ago "Debug failing test in classifier module"
|
|
49
|
+
e4f5g6h ~/projects/learnio 3 days ago "Add new math lesson generator"
|
|
50
|
+
|
|
51
|
+
# Export the session you want to take with you
|
|
52
|
+
commuter export a1b2c3d -o ~/Dropbox/session.json
|
|
53
|
+
|
|
54
|
+
✓ Exported session a1b2c3d (47 messages, 12KB)
|
|
55
|
+
✓ Git snapshot: branch feature/new-classifier @ a1b2c3d (2 dirty files)
|
|
56
|
+
✓ Saved to /home/ljubomir/Dropbox/session.json
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Commute
|
|
60
|
+
|
|
61
|
+
Your session file syncs to Dropbox automatically. You can check on your phone that it arrived if you're anxious, but there's nothing to do.
|
|
62
|
+
|
|
63
|
+
### Arrive at office
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
# Dropbox has already synced the file. Import it:
|
|
67
|
+
commuter import ~/Dropbox/session.json
|
|
68
|
+
|
|
69
|
+
✓ Detected project: /home/ljubomir/projects/trivertiy-ml
|
|
70
|
+
✓ Mapped to local path: /Users/ljubomir/projects/trivertiy-ml
|
|
71
|
+
✓ Git check: branch feature/new-classifier @ a1b2c3d ✓ (matches export)
|
|
72
|
+
⚠ 2 dirty files in export not present locally — did you commit before leaving?
|
|
73
|
+
- src/model.py
|
|
74
|
+
- tests/test_model.py
|
|
75
|
+
✓ Restored conversation (47 messages)
|
|
76
|
+
✓ Restored project config (.claude/settings.json, CLAUDE.md)
|
|
77
|
+
|
|
78
|
+
Launching Claude Code with restored session...
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Claude Code opens. It has the full conversation context — it remembers everything you discussed, the files you edited, the decisions you made. But now it's running locally on your office laptop, reading and writing your local files.
|
|
82
|
+
|
|
83
|
+
You pick up exactly where you left off:
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
You: Let's continue with the test fix. What was the last error we saw?
|
|
87
|
+
Claude: We were debugging the assertion failure in test_classifier.py line 42...
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### End of day: Back home (round-trip)
|
|
91
|
+
|
|
92
|
+
You worked all day at the office. Now you want to continue at home tonight.
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
# At office, leaving:
|
|
96
|
+
commuter export --latest -o ~/Dropbox/session.json
|
|
97
|
+
|
|
98
|
+
✓ Exported session a1b2c3d (112 messages, 28KB)
|
|
99
|
+
✓ Saved to /Users/ljubomir/Dropbox/session.json
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Commute home. At your home desktop:
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
commuter import ~/Dropbox/session.json
|
|
106
|
+
|
|
107
|
+
✓ Detected project: /Users/ljubomir/projects/trivertiy-ml
|
|
108
|
+
✓ Mapped to local path: /home/ljubomir/projects/trivertiy-ml
|
|
109
|
+
✓ Session continuity: imported session is a continuation of local session a1b2c3d
|
|
110
|
+
Local: 47 messages (exported 10h ago)
|
|
111
|
+
Imported: 112 messages (last active 20 min ago)
|
|
112
|
+
✓ Replacing local session with imported version
|
|
113
|
+
✓ Restored conversation (112 messages)
|
|
114
|
+
|
|
115
|
+
Launching Claude Code with restored session...
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
When the imported session is a **continuation** of an existing local session (the imported conversation starts with the same messages as the local one, just has more), the tool replaces it automatically without prompting. This is the expected round-trip case.
|
|
119
|
+
|
|
120
|
+
Use `--replace` to force replacement when continuity can't be auto-detected:
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
commuter import ~/Dropbox/session.json --replace
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Shortcut version (stretch goal)
|
|
127
|
+
|
|
128
|
+
If you configure a shared transfer directory:
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
commuter config set transfer-dir ~/Dropbox/.commuter/
|
|
132
|
+
|
|
133
|
+
# Then every day:
|
|
134
|
+
commuter push # exports latest session to transfer dir
|
|
135
|
+
commuter pull # imports from transfer dir, replaces if continuation
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Push/pull defaults to `--replace` behavior since the entire point is frictionless round-trips.
|
|
139
|
+
|
|
140
|
+
### Error cases the user might hit
|
|
141
|
+
|
|
142
|
+
**Git state mismatch:**
|
|
143
|
+
```bash
|
|
144
|
+
commuter import ~/Dropbox/session.json
|
|
145
|
+
|
|
146
|
+
⚠ WARNING: Git state differs from export
|
|
147
|
+
Export: feature/new-classifier @ a1b2c3d
|
|
148
|
+
Local: main @ f7g8h9i
|
|
149
|
+
Continue anyway? Claude will have context from a different branch. [y/N]
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
**Project not found:**
|
|
153
|
+
```bash
|
|
154
|
+
commuter import ~/Dropbox/session.json
|
|
155
|
+
|
|
156
|
+
✗ Project path /home/ljubomir/projects/trivertiy-ml not found
|
|
157
|
+
✗ No path mapping matched
|
|
158
|
+
Specify local project directory:
|
|
159
|
+
commuter import ~/Dropbox/session.json --project-dir /path/to/project
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
**Unrelated session already exists (NOT a continuation):**
|
|
163
|
+
```bash
|
|
164
|
+
commuter import ~/Dropbox/session.json
|
|
165
|
+
|
|
166
|
+
⚠ A different session already exists for this project (last active 15 min ago)
|
|
167
|
+
Local session has divergent conversation history — this is not a continuation.
|
|
168
|
+
Overwrite local session with imported one? [y/N]
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## Assumptions
|
|
172
|
+
|
|
173
|
+
- The project filesystem is already replicated across machines (Git, Dropbox, or similar). The tool does NOT handle file sync.
|
|
174
|
+
- Claude Code is installed on both machines.
|
|
175
|
+
- The user has the same Claude Code authentication on both machines.
|
|
176
|
+
- Paths to the project directory may differ between machines (e.g., `/home/user/projects/foo` on Linux vs `/Users/user/projects/foo` on macOS).
|
|
177
|
+
|
|
178
|
+
## Architecture
|
|
179
|
+
|
|
180
|
+
A standalone Python CLI tool distributed via PyPI. No dependency on Claude Code internals beyond the session storage format. The tool operates in three phases: discover, export, import.
|
|
181
|
+
|
|
182
|
+
The session discovery, export, and import logic should be isolated behind a **backend interface** so that future backends (Codex CLI, Gemini CLI, etc.) can be added without changing the CLI or bundle format. The initial implementation provides only the Claude Code backend.
|
|
183
|
+
|
|
184
|
+
## Core Features
|
|
185
|
+
|
|
186
|
+
### 1. Session Discovery
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
commuter list
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
- Discover and list all Claude Code sessions on the current machine.
|
|
193
|
+
- Show: session ID, project directory, last activity timestamp, conversation summary (first/last message preview).
|
|
194
|
+
- Research where Claude Code stores session data. Likely locations:
|
|
195
|
+
- `~/.claude/` or `~/.config/claude-code/`
|
|
196
|
+
- Project-local `.claude/` directory
|
|
197
|
+
- Check both, document findings.
|
|
198
|
+
|
|
199
|
+
### 2. Session Export
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
commuter export [session-id] -o session-bundle.json
|
|
203
|
+
commuter export --latest -o session-bundle.json
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
Export a session into a portable JSON bundle containing:
|
|
207
|
+
|
|
208
|
+
- **Conversation history**: The full message log (user messages, assistant responses, tool calls and results).
|
|
209
|
+
- **Project path**: The original absolute path to the working directory.
|
|
210
|
+
- **Project config**: Contents of relevant config files (`.claude/settings.json`, `CLAUDE.md`, `.claude/commands/` if present).
|
|
211
|
+
- **Git state** (if applicable): Current branch, commit hash, dirty file list (as a reference snapshot, not the files themselves).
|
|
212
|
+
- **Environment metadata**: OS, Claude Code version, timestamp, hostname.
|
|
213
|
+
- **Session config**: Any session-specific settings (model, permissions, MCP server configs).
|
|
214
|
+
- **Lineage info**: A hash of the first N messages in the conversation, used to detect whether an imported session is a continuation of a local one (see Session Continuity below).
|
|
215
|
+
|
|
216
|
+
The bundle should be a single JSON file, human-readable, reasonably compact. Use gzip compression as an option for large sessions.
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
commuter export --latest -o session-bundle.json.gz --compress
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### 3. Session Import
|
|
223
|
+
|
|
224
|
+
```bash
|
|
225
|
+
commuter import session-bundle.json [--project-dir /path/to/local/project]
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
- Reconstruct the session on the target machine.
|
|
229
|
+
- If `--project-dir` is not specified, attempt to auto-detect: check if the original path exists, then try common path substitutions (see Path Mapping below).
|
|
230
|
+
- Inject the conversation history so Claude Code can resume with full context.
|
|
231
|
+
- Restore project config files to the local `.claude/` directory (with confirmation prompt if files already exist).
|
|
232
|
+
- Validate that the local project state is compatible: warn if the git branch or commit differs from the export snapshot.
|
|
233
|
+
- Launch Claude Code with the restored session using `--resume` or `--continue` (determine which flag is appropriate).
|
|
234
|
+
|
|
235
|
+
**Flags:**
|
|
236
|
+
- `--replace`: Force replacement of an existing local session without prompting.
|
|
237
|
+
- `--no-launch`: Import the session but don't launch Claude Code.
|
|
238
|
+
- `--dry-run`: Show what would happen without making changes.
|
|
239
|
+
|
|
240
|
+
### 4. Session Continuity Detection
|
|
241
|
+
|
|
242
|
+
When importing, the tool checks whether a session for the same project already exists locally. If it does, it computes whether the imported session is a **continuation** of the local one:
|
|
243
|
+
|
|
244
|
+
- Compute a hash of the first N messages (e.g., first 10) in both the local and imported conversations.
|
|
245
|
+
- If the hashes match and the imported session has MORE messages, it's a continuation → replace automatically.
|
|
246
|
+
- If the hashes don't match, the sessions have diverged → prompt for confirmation (unless `--replace` is set).
|
|
247
|
+
- If no local session exists for that project → import directly, no prompt needed.
|
|
248
|
+
|
|
249
|
+
This makes the daily home → office → home round-trip seamless: no confirmation prompts, no flags needed.
|
|
250
|
+
|
|
251
|
+
### 5. Path Mapping
|
|
252
|
+
|
|
253
|
+
```bash
|
|
254
|
+
commuter config set path-map "/home/ljubomir/projects" "/Users/ljubomir/projects"
|
|
255
|
+
commuter config set path-map "/home/ljubomir/Dropbox" "/Users/ljubomir/Dropbox"
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
- Store path mappings in `~/.config/commuter/config.json`.
|
|
259
|
+
- Applied automatically during import to translate paths in the session bundle.
|
|
260
|
+
- Bidirectional: the tool infers direction based on which side matches the imported path.
|
|
261
|
+
- Support multiple mappings, applied in order of specificity (longest prefix first).
|
|
262
|
+
|
|
263
|
+
### 6. Session Transfer Shortcut (stretch goal)
|
|
264
|
+
|
|
265
|
+
```bash
|
|
266
|
+
# On source machine:
|
|
267
|
+
commuter push # exports latest to configured transfer dir
|
|
268
|
+
|
|
269
|
+
# On target machine:
|
|
270
|
+
commuter pull # imports from transfer dir, auto-replaces continuations
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
- Requires `transfer-dir` to be configured: `commuter config set transfer-dir ~/Dropbox/.commuter/`
|
|
274
|
+
- `push` exports the most recent session to `<transfer-dir>/pending/`.
|
|
275
|
+
- `pull` imports from `<transfer-dir>/pending/` and moves the bundle to `<transfer-dir>/history/` after successful import.
|
|
276
|
+
- `pull` defaults to `--replace` behavior for continuations.
|
|
277
|
+
- History directory provides an audit trail and rollback capability.
|
|
278
|
+
|
|
279
|
+
## CLI Design
|
|
280
|
+
|
|
281
|
+
- Built with `click` or `argparse` (prefer `click`).
|
|
282
|
+
- Colored terminal output using `rich` for session listings and status messages.
|
|
283
|
+
- Confirmation prompts before overwriting existing config or sessions (unless auto-detected as continuation).
|
|
284
|
+
- `--dry-run` flag on import to show what would be changed without doing it.
|
|
285
|
+
- `--verbose` / `-v` flag for debug output.
|
|
286
|
+
- `--quiet` / `-q` flag for script-friendly output.
|
|
287
|
+
|
|
288
|
+
## Session Bundle Schema
|
|
289
|
+
|
|
290
|
+
```json
|
|
291
|
+
{
|
|
292
|
+
"version": "1.0",
|
|
293
|
+
"tool": "commuter",
|
|
294
|
+
"backend": "claude-code",
|
|
295
|
+
"exported_at": "2026-03-03T10:30:00Z",
|
|
296
|
+
"source": {
|
|
297
|
+
"hostname": "home-desktop",
|
|
298
|
+
"os": "Linux",
|
|
299
|
+
"backend_version": "1.x.x",
|
|
300
|
+
"username": "ljubomir"
|
|
301
|
+
},
|
|
302
|
+
"session": {
|
|
303
|
+
"id": "abc123",
|
|
304
|
+
"project_dir": "/home/ljubomir/projects/trivertiy-ml",
|
|
305
|
+
"started_at": "2026-03-03T09:00:00Z",
|
|
306
|
+
"last_activity": "2026-03-03T10:25:00Z",
|
|
307
|
+
"message_count": 47,
|
|
308
|
+
"lineage_hash": "sha256:abcdef1234...",
|
|
309
|
+
"conversation": [
|
|
310
|
+
// Full conversation history array
|
|
311
|
+
],
|
|
312
|
+
"config": {
|
|
313
|
+
"settings_json": {},
|
|
314
|
+
"claude_md": "contents of CLAUDE.md",
|
|
315
|
+
"commands": {}
|
|
316
|
+
}
|
|
317
|
+
},
|
|
318
|
+
"git_snapshot": {
|
|
319
|
+
"branch": "feature/new-classifier",
|
|
320
|
+
"commit": "a1b2c3d",
|
|
321
|
+
"dirty_files": ["src/model.py", "tests/test_model.py"]
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
## Research Tasks (Do First)
|
|
327
|
+
|
|
328
|
+
Before writing code, investigate and document:
|
|
329
|
+
|
|
330
|
+
1. **Where does Claude Code store session/conversation data?** Check `~/.claude/`, `~/.config/claude-code/`, project `.claude/` dirs, and any SQLite databases or JSON files.
|
|
331
|
+
2. **What format is the conversation history in?** Need to understand the schema to export/import it correctly.
|
|
332
|
+
3. **What CLI flags does Claude Code support for resuming sessions?** Test `--resume`, `--continue`, and any other relevant flags. Determine if there's a way to inject conversation history programmatically.
|
|
333
|
+
4. **Is the session data self-contained or does it reference external state?** (e.g., API-side session IDs that can't be transferred)
|
|
334
|
+
|
|
335
|
+
If Claude Code stores sessions server-side and there's no local conversation log, the approach needs to change: we'd capture the conversation via a wrapper/proxy instead. Document findings before proceeding with implementation.
|
|
336
|
+
|
|
337
|
+
## Tech Stack
|
|
338
|
+
|
|
339
|
+
- Python 3.10+
|
|
340
|
+
- `click` for CLI
|
|
341
|
+
- `rich` for terminal formatting
|
|
342
|
+
- No other heavy dependencies
|
|
343
|
+
- Standard library for JSON, gzip, pathlib, hashlib, etc.
|
|
344
|
+
|
|
345
|
+
## Distribution
|
|
346
|
+
|
|
347
|
+
- Package name: `commuter`
|
|
348
|
+
- Published to PyPI
|
|
349
|
+
- Installable via: `pip install commuter`
|
|
350
|
+
- Entry point: `commuter` command
|
|
351
|
+
- License: MIT
|
|
352
|
+
|
|
353
|
+
## Project Structure
|
|
354
|
+
|
|
355
|
+
```
|
|
356
|
+
commuter/
|
|
357
|
+
├── pyproject.toml
|
|
358
|
+
├── README.md
|
|
359
|
+
├── LICENSE
|
|
360
|
+
├── src/
|
|
361
|
+
│ └── commuter/
|
|
362
|
+
│ ├── __init__.py
|
|
363
|
+
│ ├── cli.py # Click CLI entry point
|
|
364
|
+
│ ├── config.py # User config management
|
|
365
|
+
│ ├── bundle.py # Bundle schema, validation, versioning
|
|
366
|
+
│ ├── pathmap.py # Path mapping config and translation
|
|
367
|
+
│ ├── lineage.py # Session continuity detection
|
|
368
|
+
│ ├── git_utils.py # Git state snapshot
|
|
369
|
+
│ └── backends/
|
|
370
|
+
│ ├── __init__.py # Backend interface / base class
|
|
371
|
+
│ └── claude_code.py # Claude Code: discover, export, import
|
|
372
|
+
├── tests/
|
|
373
|
+
│ ├── test_cli.py
|
|
374
|
+
│ ├── test_bundle.py
|
|
375
|
+
│ ├── test_pathmap.py
|
|
376
|
+
│ ├── test_lineage.py
|
|
377
|
+
│ ├── test_claude_code.py
|
|
378
|
+
│ └── fixtures/ # Sample session data for testing
|
|
379
|
+
└── .claude/
|
|
380
|
+
└── CLAUDE.md # This file, for Claude Code context
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
## Testing
|
|
384
|
+
|
|
385
|
+
- Use `pytest`.
|
|
386
|
+
- Mock Claude Code session data in fixtures (based on findings from Research Tasks).
|
|
387
|
+
- Test path mapping with Linux ↔ macOS path patterns.
|
|
388
|
+
- Test path mapping bidirectionality (A→B and B→A with same config).
|
|
389
|
+
- Test git snapshot validation (matching vs. diverged states).
|
|
390
|
+
- Test bundle schema validation and version compatibility.
|
|
391
|
+
- Test export → import round-trip preserves all data.
|
|
392
|
+
- Test session continuity detection: continuation (auto-replace), divergence (prompt), no existing session (direct import).
|
|
393
|
+
- Test push/pull with mock transfer directory.
|
|
394
|
+
|
|
395
|
+
## Out of Scope (for now)
|
|
396
|
+
|
|
397
|
+
- File synchronization between machines (user's responsibility).
|
|
398
|
+
- Multi-user session sharing.
|
|
399
|
+
- Running process migration (only conversation + config state).
|
|
400
|
+
- MCP server auto-configuration on the target machine (document what MCP servers were active; user sets them up).
|
|
401
|
+
- Non-Claude-Code backends (architecture supports them; implementation is future work).
|
|
402
|
+
|
|
403
|
+
## Success Criteria
|
|
404
|
+
|
|
405
|
+
1. Export a session on Machine A where I've been working for 30+ minutes with Claude Code.
|
|
406
|
+
2. Import it on Machine B (which has the same project via Git/Dropbox).
|
|
407
|
+
3. Claude Code on Machine B has full conversation context and continues working seamlessly, with local file access on Machine B.
|
|
408
|
+
4. Export from Machine B at end of day, import back on Machine A — round-trip works without prompts.
|
|
409
|
+
5. The whole transfer takes under 30 seconds.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "commuter"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Portable AI coding session transfer between machines"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = {text = "MIT"}
|
|
11
|
+
requires-python = ">=3.10"
|
|
12
|
+
dependencies = [
|
|
13
|
+
"click>=8.0",
|
|
14
|
+
"rich>=13.0",
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
[project.scripts]
|
|
18
|
+
commuter = "commuter.cli:cli"
|
|
19
|
+
|
|
20
|
+
[tool.hatch.build.targets.wheel]
|
|
21
|
+
packages = ["src/commuter"]
|
|
22
|
+
|
|
23
|
+
[tool.pytest.ini_options]
|
|
24
|
+
testpaths = ["tests"]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.0"
|