diff --git a/CHANGELOG.md b/CHANGELOG.md index e186981..e8ad350 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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_` and `_ai_save_` functions +- `GIT_WT_COPY_AI` environment variable to permanently enable AI integration + ## [1.2.0] - 2026-02-24 ### Added diff --git a/README.md b/README.md index b6e78af..ba7d995 100644 --- a/README.md +++ b/README.md @@ -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//.ai-sessions//`, syncs settings back to origin +- **Extensible**: add new providers via `GIT_WT_AI_PROVIDERS` (define `_ai_copy_` + `_ai_save_` 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`). @@ -132,6 +161,7 @@ Options for `git wt new`: | `-p, --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: @@ -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 @@ -246,6 +278,7 @@ 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 | @@ -253,6 +286,8 @@ cp skill/SKILL.md .cursor/skills/git-wt/SKILL.md - **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//` 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 diff --git a/bin/git-wt b/bin/git-wt index fa84f38..02ee8f5 100755 --- a/bin/git-wt +++ b/bin/git-wt @@ -8,7 +8,7 @@ # 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}" @@ -16,6 +16,7 @@ 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) --- @@ -176,6 +177,72 @@ _wt_names() { done < <(git worktree list --porcelain 2>/dev/null && echo "") } +# --- AI provider integration --- +# Extensible system: add _ai_copy_ + _ai_save_ 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// + 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="" @@ -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 @@ -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}" @@ -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 @@ -544,16 +627,20 @@ ${BOLD}Options for new:${RESET} -p, --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} diff --git a/completions/_git-wt b/completions/_git-wt index b275f42..cf34775 100644 --- a/completions/_git-wt +++ b/completions/_git-wt @@ -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) diff --git a/completions/git-wt.bash b/completions/git-wt.bash index 5919599..afb4130 100644 --- a/completions/git-wt.bash +++ b/completions/git-wt.bash @@ -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 diff --git a/skills/git-wt/SKILL.md b/skills/git-wt/SKILL.md index 574aae5..93f1aa6 100644 --- a/skills/git-wt/SKILL.md +++ b/skills/git-wt/SKILL.md @@ -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) ``` @@ -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 @@ -93,6 +97,11 @@ 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 @@ -100,16 +109,16 @@ Use `git wt adopt` when: ## 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 ```