Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Changelog

## [1.3.0] - 2026-02-25

### Added
- `--copy-ai` flag for `git wt new` — copies `.claude/settings.local.json` into worktree
- AI session preservation on `git wt rm` — archives Claude Code sessions, syncs settings back to origin
- Extensible provider system (`GIT_WT_AI_PROVIDERS`) — add support for new AI tools by defining `_ai_copy_<name>` and `_ai_save_<name>` functions
- `GIT_WT_COPY_AI` environment variable to permanently enable AI integration

## [1.2.0] - 2026-02-24

### Added
Expand Down
35 changes: 35 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,35 @@ git wt new --copy-env my-feature

This way your dev server starts immediately without missing config.

### AI session preservation

AI tools like Claude Code store sessions and settings per project path. When a worktree is deleted, that data is normally lost. Use `--copy-ai` to preserve it:

```bash
# On create: copies .claude/settings.local.json into worktree
git wt new --copy-ai my-feature

# Work in the worktree — AI tools create sessions, you approve new commands...

# On rm: archives Claude sessions, syncs settings back to origin
git wt rm my-feature
# Archived Claude sessions → .ai-sessions/my-feature/claude/
# Synced .claude/settings.local.json → origin
```

**What happens:**
- **On create**: copies `.claude/settings.local.json` (approved commands) into the worktree
- **On remove**: archives Claude Code sessions to `~/.git-wt/<repo>/.ai-sessions/<name>/`, syncs settings back to origin
- **Extensible**: add new providers via `GIT_WT_AI_PROVIDERS` (define `_ai_copy_<name>` + `_ai_save_<name>` functions in the script)

To enable this permanently:

```bash
export GIT_WT_COPY_AI=true # add to ~/.zshrc or ~/.bashrc
```

The provider system is extensible — see `GIT_WT_AI_PROVIDERS` to control which AI tools are managed.

### External worktrees

git-wt can see and manage worktrees created outside of `git wt` (e.g., via `git worktree add`).
Expand Down Expand Up @@ -132,6 +161,7 @@ Options for `git wt new`:
| `-p, --prefix <prefix>` | Branch prefix (default: `wt`) |
| `--no-branch` | Create with detached HEAD instead of a new branch |
| `--copy-env` | Copy `.env*` files from the repo root into the worktree |
| `--copy-ai` | Copy AI agent configs and save sessions on rm |

Global flags:

Expand All @@ -145,6 +175,8 @@ Global flags:
|----------|---------|-------------|
| `GIT_WT_HOME` | `~/.git-wt` | Root directory for all worktrees |
| `GIT_WT_PREFIX` | `wt` | Branch name prefix |
| `GIT_WT_COPY_AI` | `false` | Always copy AI configs on new, save sessions on rm |
| `GIT_WT_AI_PROVIDERS` | `claude` | Space-separated list of AI providers to manage |

## Shell Completions

Expand Down Expand Up @@ -246,13 +278,16 @@ cp skill/SKILL.md .cursor/skills/git-wt/SKILL.md
| Shell completions | ✅ bash + zsh | ✅ | ❌ | ❌ | ✅ |
| Editor integration | ✅ Cursor/VS Code | ❌ | ✅ | ❌ | ❌ |
| Agent skill (SKILL.md) | ✅ `npx skills add` | ❌ | ❌ | ❌ | ❌ |
| AI session preservation | ✅ `--copy-ai` | ❌ | ❌ | ❌ | ❌ |
| Multi-agent workflows | ✅ | ❌ | ✅ | ✅ Claude-only | ❌ |
| Install time | ~5 seconds | ~30 seconds | ~10 seconds | ~15 seconds | Built-in |

## Known Issues

- **Warp Terminal**: Tab completions for `git wt` don't work. Warp uses its own completion engine and [doesn't delegate to shell completions](https://github.com/warpdotdev/Warp/discussions/434). Completions work correctly in Terminal.app, iTerm2, Ghostty, Kitty, and other terminals that use native zsh/bash completion.

- **Codex session history**: Codex manages its own worktrees at `~/.codex/worktrees/<hash>/` and deletes them after each session. The `/resume` command can't find old sessions because their `cwd` paths no longer exist. This is a Codex-side limitation — `--copy-ai` does not cover Codex since its sessions are not tied to project paths.

## Uninstall

```bash
Expand Down
89 changes: 88 additions & 1 deletion bin/git-wt
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@
# shellcheck disable=SC2059 # intentional: ANSI color codes in printf format strings
set -euo pipefail

GIT_WT_VERSION="1.2.0"
GIT_WT_VERSION="1.3.0"

# --- Config ---
GIT_WT_HOME="${GIT_WT_HOME:-${HOME}/.git-wt}"
WT_PREFIX="${GIT_WT_PREFIX:-wt}"
WT_BASE=""
WT_NO_BRANCH=false
WT_COPY_ENV=false
WT_COPY_AI="${GIT_WT_COPY_AI:-false}"
WT_QUIET=false

# --- Colors (respect NO_COLOR and non-tty) ---
Expand Down Expand Up @@ -176,6 +177,72 @@ _wt_names() {
done < <(git worktree list --porcelain 2>/dev/null && echo "")
}

# --- AI provider integration ---
# Extensible system: add _ai_copy_<name> + _ai_save_<name> functions for new providers.

# Encode absolute path to Claude project ID: /a/b/c → -a-b-c
_claude_project_id() { echo "${1//\//-}"; }

# Copy AI configs from origin to new worktree
_ai_copy_all() {
local origin="$1" wt_path="$2"
local provider
for provider in ${GIT_WT_AI_PROVIDERS:-claude}; do
if declare -f "_ai_copy_${provider}" >/dev/null 2>&1; then
"_ai_copy_${provider}" "$origin" "$wt_path"
fi
done
touch "$wt_path/.git-wt-ai-sync"
}

# Save AI data before worktree removal
_ai_save_all() {
local wt_path="$1" wt_name="$2" archive_dir="$3" origin="$4"
local provider
for provider in ${GIT_WT_AI_PROVIDERS:-claude}; do
if declare -f "_ai_save_${provider}" >/dev/null 2>&1; then
"_ai_save_${provider}" "$wt_path" "$wt_name" "$archive_dir" "$origin"
fi
done
}

# --- Provider: Claude Code ---

_ai_copy_claude() {
local origin="$1" wt_path="$2"
local src="${origin}/.claude/settings.local.json"
if [[ -f "$src" ]]; then
mkdir -p "${wt_path}/.claude"
cp "$src" "${wt_path}/.claude/"
info " Copied .claude/settings.local.json"
fi
}

_ai_save_claude() {
local wt_path="$1" wt_name="$2" archive_dir="$3" origin="$4"

# Archive sessions from ~/.claude/projects/<encoded-path>/
local project_id
project_id=$(_claude_project_id "$wt_path")
local claude_project="${HOME}/.claude/projects/${project_id}"
if [[ -d "$claude_project" ]]; then
mkdir -p "${archive_dir}/claude"
mv "$claude_project" "${archive_dir}/claude/sessions"
info " Archived Claude sessions → .ai-sessions/${wt_name}/claude/"
fi

# Sync settings.local.json back to origin if changed
local wt_settings="${wt_path}/.claude/settings.local.json"
local origin_settings="${origin}/.claude/settings.local.json"
if [[ -f "$wt_settings" ]]; then
if [[ ! -f "$origin_settings" ]] || ! diff -q "$wt_settings" "$origin_settings" >/dev/null 2>&1; then
mkdir -p "${origin}/.claude"
cp "$wt_settings" "$origin_settings"
info " Synced .claude/settings.local.json → origin"
fi
fi
}

# --- Commands ---
cmd_new() {
local name=""
Expand All @@ -187,6 +254,7 @@ cmd_new() {
-p|--prefix) WT_PREFIX="$2"; shift 2 ;;
--no-branch) WT_NO_BRANCH=true; shift ;;
--copy-env) WT_COPY_ENV=true; shift ;;
--copy-ai) WT_COPY_AI=true; shift ;;
-*) die "unknown option: $1" ;;
*) positional+=("$1"); shift ;;
esac
Expand Down Expand Up @@ -231,6 +299,13 @@ cmd_new() {
fi
fi

# Copy AI agent configs if requested
if [[ "$WT_COPY_AI" == "true" ]]; then
local ai_origin
ai_origin=$(get_repo_root)
_ai_copy_all "$ai_origin" "$wt_path"
fi

echo ""
printf "${BOLD}Worktree ready:${RESET}\n"
printf " Path: %s\n" "${wt_path}"
Expand Down Expand Up @@ -360,6 +435,14 @@ cmd_rm() {
local branch
branch=$(cd "$wt_path" && git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "")

# Save AI agent data before removal
if [[ -f "$wt_path/.git-wt-ai-sync" ]] || [[ "$WT_COPY_AI" == "true" ]]; then
local archive_dir="${wt_root}/.ai-sessions/${name}"
local repo_root
repo_root=$(get_repo_root)
_ai_save_all "$wt_path" "$name" "$archive_dir" "$repo_root"
fi

info "Removing worktree '${name}'..."
git worktree remove "$wt_path" --force

Expand Down Expand Up @@ -544,16 +627,20 @@ ${BOLD}Options for new:${RESET}
-p, --prefix <prefix> Branch prefix (default: wt/)
--no-branch Use detached HEAD
--copy-env Copy .env* files into new worktree
--copy-ai Copy AI agent configs and save sessions on rm

${BOLD}Environment:${RESET}
GIT_WT_HOME Worktrees root (default: ~/.git-wt)
GIT_WT_PREFIX Default branch prefix (default: wt)
GIT_WT_COPY_AI Always copy AI configs on new, save on rm (default: false)
GIT_WT_AI_PROVIDERS Space-separated AI providers (default: "claude")

${BOLD}Examples:${RESET}
git wt new ${DIM}# ~/.git-wt/myrepo/swift-jade/${RESET}
git wt new my-feature ${DIM}# ~/.git-wt/myrepo/my-feature/${RESET}
git wt new -b main hotfix ${DIM}# fork from main${RESET}
git wt new --copy-env experiment ${DIM}# also copies .env files${RESET}
git wt new --copy-ai feature ${DIM}# copies AI configs, saves sessions on rm${RESET}
cd \$(git wt path my-feature) ${DIM}# jump into worktree${RESET}
git wt open my-feature ${DIM}# open in Cursor/VS Code${RESET}
git wt adopt ../my-hotfix ${DIM}# adopt external worktree${RESET}
Expand Down
1 change: 1 addition & 0 deletions completions/_git-wt
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ _git-wt() {
'(-p --prefix)'{-p,--prefix}'[Branch prefix]:prefix:' \
'--no-branch[Use detached HEAD]' \
'--copy-env[Copy .env files into worktree]' \
'--copy-ai[Copy AI agent configs and save sessions on rm]' \
':name:'
;;
adopt)
Expand Down
2 changes: 1 addition & 1 deletion completions/git-wt.bash
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ _git_wt() {
;;
*)
if [[ "$cur" == -* ]]; then
COMPREPLY=($(compgen -W "-b --base -p --prefix --no-branch --copy-env" -- "$cur"))
COMPREPLY=($(compgen -W "-b --base -p --prefix --no-branch --copy-env --copy-ai" -- "$cur"))
fi
;;
esac
Expand Down
17 changes: 13 additions & 4 deletions skills/git-wt/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ git wt new # Auto-named (e.g., swift-jade)
git wt new my-feature # Named
git wt new -b main hotfix # Fork from specific branch
git wt new --copy-env experiment # Copy .env* files into worktree
git wt new --copy-ai my-feature # Copy AI configs, save sessions on rm
git wt new --no-branch scratch # Detached HEAD (no branch created)
```

Expand Down Expand Up @@ -74,12 +75,15 @@ git wt clean # Remove all managed worktrees for current
- **`adopt`**: moves an external worktree under `~/.git-wt/` so git-wt fully manages it
- **External worktrees**: `list`, `path`, `open` work with worktrees created outside git-wt
- **`--copy-env`**: copies all `.env*` files from repo root (critical for dev servers)
- **`--copy-ai`**: copies AI configs on create, archives sessions + syncs settings on rm
- **`origin`**: prints main repo path — works from any worktree or main repo itself

## Environment Variables

- `GIT_WT_HOME` — Root directory for all worktrees (default: `~/.git-wt`)
- `GIT_WT_PREFIX` — Branch name prefix (default: `wt`)
- `GIT_WT_COPY_AI` — Always copy AI configs on new, save sessions on rm (default: `false`)
- `GIT_WT_AI_PROVIDERS` — Space-separated AI providers to manage (default: `claude`)

## When to Use

Expand All @@ -93,23 +97,28 @@ Use `git wt new --copy-env` when:
- The project has `.env` files needed for the dev server to start
- The worktree needs the same configuration as the main repo

Use `git wt new --copy-ai` when:
- Working with Claude Code or other AI tools in worktrees
- You want approved commands (`.claude/settings.local.json`) available immediately
- You want Claude sessions archived (not lost) when the worktree is removed

Use `git wt adopt` when:
- A worktree was created with `git worktree add` and you want git-wt to manage it
- You want the worktree moved to `~/.git-wt/` for consistent management

## Workflow: Parallel Agent Isolation

```bash
# Agent 1: create isolated worktree
git wt new --copy-env task-auth
# Agent 1: create isolated worktree with AI config
git wt new --copy-env --copy-ai task-auth

# Agent 2: create another
git wt new --copy-env task-api
git wt new --copy-env --copy-ai task-api

# Each agent works independently in their worktree
cd $(git wt path task-auth)

# When done
# When done — sessions archived, settings synced back
git wt rm task-auth
git wt rm task-api
```