Skip to content

feat: normalize project paths for cross-device sync#30

Open
merv-nexura wants to merge 3 commits into
tawanorg:mainfrom
merv-nexura:feat/cross-device-project-sync
Open

feat: normalize project paths for cross-device sync#30
merv-nexura wants to merge 3 commits into
tawanorg:mainfrom
merv-nexura:feat/cross-device-project-sync

Conversation

@merv-nexura
Copy link
Copy Markdown

Summary

  • Adds PathMapper that normalizes home directory slugs in remote storage keys to ${HOME} on push and resolves them back to the local home directory on pull
  • File content (session .jsonl, memory .md) also gets home dir normalization, following the existing MCP normalization pattern from mcp.go
  • Fixes the split-brain problem where two machines with different home directories (/Users/merv vs /Users/mervynlally) create separate project directories under ~/.claude/projects/ and can't see each other's sessions or memories

How it works

  • Push: projects/-Users-merv-nexura/memory/MEMORY.md → remote key projects/${HOME}-nexura/memory/MEMORY.md.age
  • Pull on other machine: resolves to projects/-Users-mervynlally-nexura/memory/MEMORY.md
  • Legacy keys: old remote data without ${HOME} passes through unchanged (backward compatible)
  • Prefix safety: -Users-merv won't falsely match -Users-mervynlally thanks to boundary-aware prefix checking

Files changed

File Change
internal/sync/pathmap.go New — PathMapper struct with normalize/resolve for both keys and content
internal/sync/pathmap_test.go New — 13 test functions: round-trips, cross-device, prefix collision, dot-dirs, Linux paths, legacy keys
internal/sync/sync.go Modified — wire PathMapper into Syncer, remoteKey(), localPath(), uploadFile(), downloadFile()
internal/sync/sync_push_pull_test.go Modified — 3 integration tests simulating cross-device push/pull with different home dirs

Test plan

  • make check passes (fmt, vet, tests)
  • TestCrossDeviceProjectSync — push from /Users/merv, pull on /Users/mervynlally, files land under correct project dir with resolved paths
  • TestCrossDeviceProjectSync_BothDirections — bidirectional: A→B→B→A with correct path resolution each way
  • TestCrossDeviceProjectSync_LegacyKeyPassthrough — old remote keys without ${HOME} still work
  • TestNormalizeRemoteKey_PrefixCollision/Users/merv does NOT match /Users/mervynlally
  • All existing tests pass unchanged

MervLally added 3 commits May 14, 2026 07:36
Different home directories across machines (e.g., /Users/merv vs
/Users/mervynlally) caused project data to be siloed — each device
created separate project directories under ~/.claude/projects/ and
neither could see the other's sessions or memories.

Introduces PathMapper which normalizes home directory slugs in remote
keys to ${HOME} on push and resolves them back to the local home
directory on pull. File content (session .jsonl, memory .md) also gets
the same treatment, following the existing MCP normalization pattern.

Backward compatible: legacy remote keys without ${HOME} pass through
unchanged. A boundary-aware prefix check prevents -Users-merv from
falsely matching -Users-mervynlally.
Adds `claude-sync migrate` which finds remote project keys using literal
home directory paths and replaces them with normalized ${HOME} keys. If
a normalized version already exists, the legacy key is simply deleted.

Includes --dry-run flag to preview changes without modifying storage.
Safe to run multiple times — already-normalized keys are skipped.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants