claudewheel v0.24.2 /claudewheel.mv
Edit
On this page

Move session data after a project directory rename.

#claudewheel.mv

#claudewheel.mv

Move session data after a project directory rename.

#MvResult

Counters tracking the outcome of a project-directory move operation.

#_discover_profile_dirs

python
def _discover_profile_dirs(ws: 'Workspace') -> list[Path]

Find all profile directories plus ~/.claudewheel/shared/ if it exists.

Enumerates profiles via the workspace's ProfileStore, then includes the shared store directory as a peer target (it holds the actual session data). A corrupt tokens.json raises TokenStoreError -- the uniform hard-error contract.

#_rewrite_jsonl_file

python
def _rewrite_jsonl_file(path: Path, old_path: str, new_path: str, dry_run: bool) -> int

Replace old_path with new_path in every line of a JSONL file.

Returns the number of lines where a replacement was made.

#_plan_migrations

python
def _plan_migrations(old_resolved: str, new_resolved: str, descendants: set[str]) -> list[tuple[str, str]]

Build the ordered (old, new) migration plan.

Includes old_resolved itself. Every destination is new_resolved plus the source's relative suffix. Longest old paths come first so a shorter prefix is never processed before its own descendants (prefix-shadowing prevention, same pattern as import_'s rewriters).

#_decode_rel

python
def _decode_rel(root: Path, enc: str) -> list[str]

Find every existing relative dir path under root whose encoding is enc.

The path encoding is lossy ('/', '.', and literal '-' all become '-'), so one encoded string can correspond to several real paths. All matches are returned so the caller can detect ambiguity.

#_collect_project_keys

python
def _collect_project_keys(profile_dirs: list[Path], shared_dir: Path) -> set[str]

All real-path keys under projects{} across every profile's .claude.json.

#_discover_descendants

python
def _discover_descendants(profile_dirs: list[Path], old_resolved: str, source_root: Path, known_keys: set[str]) -> set[str]

Every real project path equal to or under old_resolved that has data.

Union of (a) .claude.json projects{} keys under OLD and (b) encoded projects/ dir names that decode to a path under OLD. Encoded names are never prefix-matched directly -- the encoding is ambiguous -- so each candidate is resolved back to a real path via the known keys plus filesystem checks under the moved tree. A candidate that resolves to a sibling path (merely sharing the encoded prefix) is skipped; one that cannot be resolved to exactly one real path is a hard error.

#_verify_destinations

python
def _verify_destinations(migrations: list[tuple[str, str]], old_resolved: str, source_root: Path, new_resolved: str) -> None

Hard-error unless every descendant's destination will exist on disk.

source_root is the moved tree as it currently exists (OLD before the rename, NEW in post-hoc mode), so source_root/<suffix> existing now is equivalent to NEW/<suffix> existing at migration time. On failure, every unresolvable descendant is listed and nothing is migrated.

#_rename_project_dir

python
def _rename_project_dir(old_project: Path, new_project: Path, dry_run: bool) -> bool

Rename old_project to new_project, merging when the target exists.

Returns True when a rename or merge happened (or would happen in dry run).

#_rewrite_prefixed_path

python
def _rewrite_prefixed_path(path: str, migrations: list[tuple[str, str]]) -> str

Rewrite a real path equal to or under a migrated source path.

migrations is longest-source-first, so the most specific mapping wins.

#_update_claude_json

python
def _update_claude_json(path: Path, migrations: list[tuple[str, str]], dry_run: bool) -> tuple[int, int]

Rename project keys and rewrite githubRepoPaths in one .claude.json.

Project keys live under data["projects"]; every key matching a migration source is renamed to its destination. githubRepoPaths values (repo -> list of local paths) equal to or under a migration source are rewritten too. Returns (project_keys_updated, github_paths_updated).

#run_mv

python
def run_mv(ws: 'Workspace', old_path: str, new_path: str, dry_run: bool=False, quiet: bool=False, post_hoc: bool=False) -> MvResult

Rename a project directory and migrate Claude Code session data.

In default mode, renames old_path to new_path on the filesystem and then migrates all session data. With post_hoc=True, skips the filesystem rename (the directory was already renamed externally) and only migrates sessions.

The migration is prefix-aware: every project keyed at old_path or nested under it (Claude Code projects inside the moved tree) is migrated to new_path plus the same relative suffix. That covers the encoded projects/ dirs, the projects{} keys and githubRepoPaths entries in every profile's .claude.json, and the JSONL cwd references of every migrated project. Each descendant's destination must exist on disk under new_path; otherwise the operation aborts before touching anything.

Search