fix(cloud): remap project paths across machines via git remote#204
fix(cloud): remap project paths across machines via git remote#204vakovalskii merged 1 commit intomainfrom
Conversation
Cloud Sync previously preserved the source machine's absolute project
path and derived the Claude/Cursor project key from it on pull. That
tied project identity to the source machine, so sessions pushed from
e.g. /Users/alice/work/app on macOS were restored under a Claude
project bucket that did not exist on a Linux machine where the same
repo lived at /home/bob/src/app.
Changes:
- serialize: capture normalized git remote URL + git root alongside
the source path; bump canonical version 1 → 2
- deserialize (claude + cursor):
1. If a local checkout exists whose git remote matches, write into
that project's key (session shows up under the right project)
2. Else if gitRemote is known, write into a neutral bucket
-cloud-import-{slug}/ so imports don't pollute nonexistent
source-machine path buckets
3. Else (legacy v1 payload or no git info) preserve prior behavior
- normalizeGitRemote: https/ssh/git@ variants + case + credentials +
trailing slashes/.git all map to one canonical form
- findLocalProjectByRemote: 30s-cached scan of loaded sessions
- Add unit tests for normalization helpers
NovakPAai
left a comment
There was a problem hiding this comment.
Code Review
Logic is sound, tests cover all cases — ready to merge.
A few observations:
findLocalProjectByRemote can be expensive
On first call it iterates all sessions via loadSessions() + getProjectGitInfo() per session. On 300+ sessions this is noticeable. The 30s cache helps on repeat calls, but the first pull after a dashboard restart will be slow. Consider building the map over projects only (not sessions), or logging a warning when the scan takes >1s.
ℹ️ require('./data') inside a function
Lazy require works but is non-idiomatic — usually a sign of circular dependency. Worth a one-line comment explaining why, so a future reader doesn't move it to the top and introduce a cycle.
ℹ️ Silent catch {} blocks
Understood that this is by design (don't break the main flow), but failures in git remote resolution will be hard to diagnose. Even catch (e) { /* git info unavailable */ } or a debug-level log would help.
✅ What's good:
normalizeGitRemotecorrectly handles all URL variants (https/ssh/git@, embedded credentials, .git suffix, case)- Fallback ladder (matched local → neutral bucket → legacy behavior) is safe and well-thought-out
- Payload versioning v1→v2 with backward compatibility is the right approach
- No conflicts with other open PRs
Closes #154.
Problem
Cloud Sync serialized a session with the source machine's absolute project path (e.g.
/Users/alice/work/app) and, on pull, derived the Claude/Cursor project key by sanitizing that path. So a session pushed from macOS landed on a Linux machine under a project bucket matching the mac path — even if the same repo was actually checked out at/home/bob/src/applocally. Imported sessions appeared under a logical project that didn't exist on the destination.Fix
Identity-by-git-remote, with a safe fallback ladder:
On push (
serializeSession):getProjectGitInfo(session.project)→ captureremote.origin.urlandgitRoothttps://…,git@…,ssh://…, credentials, trailing/,.git, case → single canonical form likegithub.com/user/repo)gitRemoteandgitRootto the canonical payloadversion: 1 → 2On pull (
deserializeSession, Claude + Cursor branches):gitRemoteis set and there's a local project whose remote matches → write into that project's key. Session shows up under the right local path.gitRemoteis set but no local checkout matches → write into a neutral bucket-cloud-import-{slug}/. Imports don't pollute a nonexistent source-machine path bucket.Local-project lookup is cached for 30s.
Scope / non-goals
Tests
New
test/cloud-remote-normalize.test.jscoversnormalizeGitRemote(https/ssh/git@/credentials/case/trailing slash/.git) andslugifyRemoteForDir. Run:node --test test/*.test.js.Test plan
/Users/alice/work/app) to cloud/home/bob/src/app— session appears under the bob path project~/.claude/projects/-cloud-import-github-com-…/