Skip to content
Back to skills

Mv Repo

BSecurity

Move or relocate a repo to a new directory — 'move this repo to <path>', 'relocate this project', 'move ~/code/X into ~/code/Y/'. Handles venv, Claude Code project state, symlinks, cache invalidation, path references, and tmux sessions.

  • 13 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added September 3, 2026
documentationrustbashnodegitci/cd

Works with

  • claude code

Security analysis

B77/100
  • highCreates or modifies cron jobs for persistent execution
  • mediumInstalls packages at runtime which could introduce malicious dependencies

Pro shows the line behind each finding and how to fix it

Scanned September 3, 2026

npx -y skills add yulonglin/dotfiles --skill mv-repo --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Mv Repo?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Mv Repo
[![Security: B — Skills Directory](https://www.skillsdirectory.com/api/skills/yulonglin-mv-repo/badge)](https://www.skillsdirectory.com/skills/yulonglin-mv-repo)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: mv-repo
description: "Move or relocate a repo to a new directory — 'move this repo to <path>', 'relocate this project', 'move ~/code/X into ~/code/Y/'. Handles venv, Claude Code project state, symlinks, cache invalidation, path references, and tmux sessions."
---

# Move Repo

Move one or more repos to a new parent directory, updating all path-dependent state.

**Usage:** `/mv-repo <source>... <dest-dir>`

Examples:
- `/mv-repo nudge bots/` — move `nudge` into `bots/`
- `/mv-repo nudge ambassador swordsmith bots/` — move multiple repos

## Steps

### 1. Validate, and warn before anything moves

- Confirm source repo(s) exist and dest directory exists (or create it)
- Confirm no name collisions in dest

**Inventory symlinks in both directions now** — a dangling link fails silently at read time, so the list has to be taken before the target disappears:

```bash
# Links pointing OUT of the repo (vaults, shared data dirs, linked configs)
find <source> -type l -exec ls -ld {} +

# Links pointing INTO the repo from elsewhere — these are the dangerous ones
find ~ -maxdepth 5 -type l -lname "*<source-basename>*" 2>/dev/null
```

Obsidian vaults and other synced directories are the common inbound case. A sync that reads a dangling link can interpret "target missing" as "files deleted" and propagate the deletion, so re-point these in step 6 before running any sync.

**Say which caches the move invalidates BEFORE moving, not after.** Anything keyed on the absolute project path is orphaned by the move — disk stays used, the next run is cold, and a large rebuild is a surprise the user should agree to first. Name the ones that apply here:

- `.venv/` — the absolute path is baked into `pyvenv.cfg` and every console script (step 3 recreates it)
- direnv — the allow list is keyed by path, so `direnv allow` must be re-run in the new location
- Editable installs (`uv pip install -e`, `pip install -e`) — the `.pth` entry still points at the old path
- Build caches that record absolute paths: Rust `target/`, `ccache`/`sccache`, `node_modules/.cache`, `.next`, `.turbo`
- Anything the user's own tooling pins by path: pueue task cwd, `jexp` job definitions, IDE project files

If a rebuild is expensive (a large Rust `target/`, a big model or dataset cache), say the size and confirm before continuing.

### 2. Move

```bash
mv <source>... <dest>/
```

### 3. Recreate venvs

For each moved repo that has a `.venv/`:
- Delete `.venv`
- Run `uv sync` in the new location
- If no `pyproject.toml`, skip (venv was manual — warn user)

### 4. Update Claude Code project state

Rename directories in `~/.claude/projects/` that match the old path encoding.

Path encoding: absolute path with `/` replaced by `-`, leading `-`. Example:
- `/home/yulong/code/nudge` → `-home-yulong-code-nudge`
- `/home/yulong/code/bots/nudge` → `-home-yulong-code-bots-nudge`

Include worktree variants (dirs matching `${old_encoded}--*`).

```bash
# For each repo, rename matching project dirs
# Strip trailing slash to avoid encoding artifacts
old_abs_path="${old_abs_path%/}"
new_abs_path="${new_abs_path%/}"
old_encoded="$(echo "$old_abs_path" | tr '/' '-')"
new_encoded="$(echo "$new_abs_path" | tr '/' '-')"
# Match exact dir and worktree variants (--*), not unrelated repos
for dir in ~/.claude/projects/${old_encoded} ~/.claude/projects/${old_encoded}--*; do
  [ -d "$dir" ] || continue
  new_dir="${dir/$old_encoded/$new_encoded}"
  mv "$dir" "$new_dir"
done
```

### 5. Handle git worktrees

Moving a repo breaks git worktree references in two places:

**A. Main repo's worktree registry** (`.git/worktrees/*/gitdir`):

```bash
git -C <new-path> worktree list
```

For each worktree:
- **Path doesn't exist** (stale) → `git worktree prune` removes it
- **Path exists, has changes** → update paths (see below)
- **Path exists, only `.claude/settings.json` changes** → safe to prune

**B. Claude Code worktrees** (`.claude/worktrees/*/`):

These moved with the repo but contain `.git` **files** (not directories) with absolute `gitdir:` paths pointing to the old location:

```
# .claude/worktrees/auto-heal/.git contains:
gitdir: /old/path/.git/worktrees/auto-heal
```

Fix both directions (use `-F` for fixed-string matching — paths contain `.` which is regex):
```bash
old_path="/old/path"
new_path="/new/path"

# Fix .claude/worktrees/*/.git → points to main repo's .git/worktrees/
for gitfile in <new-path>/.claude/worktrees/*/.git; do
  [ -f "$gitfile" ] || continue
  sd -F "$old_path" "$new_path" "$gitfile"
done

# Fix .git/worktrees/*/gitdir → points back to .claude/worktrees/
for gitdir_file in <new-path>/.git/worktrees/*/gitdir; do
  [ -f "$gitdir_file" ] || continue
  sd -F "$old_path" "$new_path" "$gitdir_file"
done

# Check for absolute commondir paths (should be relative, but verify)
for f in <new-path>/.git/worktrees/*/commondir; do
  [ -f "$f" ] || continue
  grep -q "^/" "$f" && echo "WARNING: absolute commondir in $f — fix manually"
done
```

If worktrees have no meaningful changes (just `settings.json`), it's simpler to `mv .claude/worktrees .claude/worktrees.bak` and let them be recreated. `*.bak` is globally gitignored.

**C. `hooksPath` in `.git/config`** (and worktree configs):

Use `git config` to scope the fix to `hooksPath` only (avoid corrupting remote URLs or other config):
```bash
# Main repo
hooks_path=$(git -C <new-path> config core.hooksPath 2>/dev/null)
if [[ "$hooks_path" == *"$old_path"* ]]; then
  git -C <new-path> config core.hooksPath "${hooks_path/$old_path/$new_path}"
fi

# Worktree-level configs may also override hooksPath
for wt_config in <new-path>/.git/worktrees/*/config; do
  [ -f "$wt_config" ] || continue
  grep -qF "$old_path" "$wt_config" && sd -F "$old_path" "$new_path" "$wt_config"
done
```

### 6. Re-point symlinks

Work from the inventory taken in step 1.

**Links pointing INTO the repo** — anything outside the repo whose target was under the old path:

```bash
for link in <inbound links from step 1>; do
  target="$(readlink "$link")"
  case "$target" in
    "$old_path"*) ln -sfn "${target/$old_path/$new_path}" "$link" ;;
  esac
done
```

**Links pointing OUT of the repo** — absolute links still resolve and need no change; relative links break if the new location sits at a different depth. Re-point only the ones that stopped resolving.

Verify nothing dangles before moving on:

```bash
find <new-path> -xtype l          # broken links inside the repo
find ~ -maxdepth 5 -xtype l -lname "*<old-path-fragment>*" 2>/dev/null
```

### 7. Grep for stale path references

Search for old paths across common locations:
- `~/code/` (all repos — CLAUDE.md, specs, scripts, configs)
- `~/.claude/` (rules, docs, settings)
- Dotfiles config dir if it exists

```bash
rg --no-ignore -l "$old_path_pattern" ~/code/ ~/.claude/ 2>/dev/null
```

For each match:
- Show the file and matching lines
- Offer to fix (replace old path with new path)
- Skip `tmp/`, `archive/`, `.git/`, `node_modules/` matches — these are throwaway

### 8. Update tmux sessions

Check for tmux sessions named after the repo:

```bash
tmux ls 2>/dev/null | grep -i "<repo-name>"
```

If found, send `cd` to the new path:

```bash
tmux send-keys -t <session> "cd <new-path>" Enter
```

### 9. Remind about manual steps

After completing all automated steps, remind the user about:
- **Crontab entries**: `crontab -l | grep <old-path>` — update manually with `crontab -e`
- **Systemd units/launchd plists**: check for hardcoded paths
- **Git commits**: each moved repo has uncommitted changes (updated refs) — commit them
- **Remote CI/CD**: any pipelines referencing the old path

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.