Skip to content
Open
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
1 change: 1 addition & 0 deletions plugins/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Learn more in the [official plugins documentation](https://docs.claude.com/en/do
| [claude-opus-4-5-migration](./claude-opus-4-5-migration/) | Migrate code and prompts from Sonnet 4.x and Opus 4.1 to Opus 4.5 | **Skill:** `claude-opus-4-5-migration` - Automated migration of model strings, beta headers, and prompt adjustments |
| [code-review](./code-review/) | Automated PR code review using multiple specialized agents with confidence-based scoring to filter false positives | **Command:** `/code-review` - Automated PR review workflow<br>**Agents:** 5 parallel Sonnet agents for CLAUDE.md compliance, bug detection, historical context, PR history, and code comments |
| [commit-commands](./commit-commands/) | Git workflow automation for committing, pushing, and creating pull requests | **Commands:** `/commit`, `/commit-push-pr`, `/clean_gone` - Streamlined git operations |
| [git-aware-history](./git-aware-history/) | Fix Claude Code session history fragmentation across git worktrees — all worktrees of a repo share one history directory | **Command:** `/consolidate-history` - End-to-end setup: installs PATH wrapper, migrates existing orphaned history<br>**Scripts:** `consolidate-git-history.sh` - Retroactive migration, `claude-wrapper.sh` - PATH wrapper template |
| [explanatory-output-style](./explanatory-output-style/) | Adds educational insights about implementation choices and codebase patterns (mimics the deprecated Explanatory output style) | **Hook:** SessionStart - Injects educational context at the start of each session |
| [feature-dev](./feature-dev/) | Comprehensive feature development workflow with a structured 7-phase approach | **Command:** `/feature-dev` - Guided feature development workflow<br>**Agents:** `code-explorer`, `code-architect`, `code-reviewer` - For codebase analysis, architecture design, and quality review |
| [frontend-design](./frontend-design/) | Create distinctive, production-grade frontend interfaces that avoid generic AI aesthetics | **Skill:** `frontend-design` - Auto-invoked for frontend work, providing guidance on bold design choices, typography, animations, and visual details |
Expand Down
9 changes: 9 additions & 0 deletions plugins/git-aware-history/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"name": "git-aware-history",
"description": "Consolidate Claude Code session history across git worktrees — all worktrees of a repo share one history directory",
"version": "1.0.0",
"author": {
"name": "Ilan Peretz",
"email": "ilan.peretz@optibus.com"
}
}
154 changes: 154 additions & 0 deletions plugins/git-aware-history/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
# Git-Aware History Plugin

Fix Claude Code session history fragmentation across git worktrees.

## The Problem

Claude Code keys session history by the working directory path (`~/.claude/projects/<slug>/`). When you use git worktrees, each worktree gets its own isolated history directory. Deleting a worktree orphans its history — `/resume` can't find it, and there's no way to see all sessions for a repo in one place.

Active worktree users end up with dozens of orphaned history directories after a sprint.

## The Solution

`git rev-parse --git-common-dir` returns the **same** `.git` path for any worktree of a repo. This plugin uses that as the canonical project identity, so all worktrees of a repo share one history directory.

## Components

### 1. PATH wrapper script (ongoing routing)

A small `claude` wrapper script installed to `~/.local/bin/claude` — **before** the real binary on PATH. It runs `git rev-parse --git-common-dir` on every invocation and symlinks the worktree's project dir to the git-root's project dir before Claude Code opens it.

**No shell function, no alias, no `.zshrc` sourcing required. Gated by a flag file so you can disable/re-enable without touching the script:** Because it sits on PATH, it works in every shell (bash, zsh, fish), every terminal, and IDE integrations that don't load shell profiles. The real `claude` binary path is baked in at install time.

```bash
touch ~/.claude/git-aware-history.enabled # enable
rm ~/.claude/git-aware-history.enabled # disable
```


### 2. Consolidation script (`consolidate-git-history.sh`)

Retroactively migrates existing orphaned history. Two phases:

- **Phase 1 — live paths**: finds all project dirs whose worktrees still exist, groups them by git root, merges and symlinks automatically
- **Phase 2 — deleted paths**: interactively handles orphaned dirs from already-deleted worktrees. Shows metadata (session count, size, date range, branch names) per group. Supports `fzf --multi` or a numbered fallback menu.

Always dry-runs first. Use `--execute` to apply.

### 3. `/consolidate-history` command

End-to-end setup in one command: installs the wrapper script to the right PATH location, migrates existing history with a dry-run preview + confirmation, and verifies everything is wired up.

## Installation

### Option A: via `/consolidate-history` command (recommended)

If you have this plugin installed, just run:
```
/consolidate-history
```

Claude will:
1. Find the real `claude` binary path
2. Install the wrapper script to `~/.local/bin/claude`
3. Verify `~/.local/bin` is on PATH before the real binary (and guide you to fix it if not)
4. Migrate your existing history with a dry-run preview

### Option B: manual

1. Copy `consolidate-git-history.sh` to `~/.claude/scripts/` and make it executable:
```bash
mkdir -p ~/.claude/scripts
cp consolidate-git-history.sh ~/.claude/scripts/
chmod +x ~/.claude/scripts/consolidate-git-history.sh
```

2. Install the PATH wrapper. First find your real `claude` binary:
```bash
which claude # e.g. /opt/homebrew/bin/claude
```

Then install the wrapper to `~/.local/bin/claude`, substituting the real path:
```bash
mkdir -p ~/.local/bin
sed "s|REAL_CLAUDE|/opt/homebrew/bin/claude|" claude-wrapper.sh > ~/.local/bin/claude
chmod +x ~/.local/bin/claude
```

3. Ensure `~/.local/bin` is before the real binary on PATH. Add to your shell profile if needed:
```bash
export PATH="$HOME/.local/bin:$PATH"
```

Verify:
```bash
which claude # should show ~/.local/bin/claude
which -a claude # should show ~/.local/bin/claude first, then the real binary
```

4. Migrate existing history:
```bash
~/.claude/scripts/consolidate-git-history.sh --dry-run
~/.claude/scripts/consolidate-git-history.sh --execute
```

## Usage

### Migrate existing history

```bash
# Dry-run (safe — no changes)
~/.claude/scripts/consolidate-git-history.sh --dry-run

# Apply
~/.claude/scripts/consolidate-git-history.sh --execute
```

During `--execute`, Phase 2 prompts you interactively for each group of orphaned sessions:

```
Orphaned sessions — inferred repo: /Users/you/dev/myrepo (inferred)
[1] -...-feature-auth | 2 sessions | 1.2MB | Apr 28 | feature/auth
[2] -...-bugfix-login | 1 session | 340KB | Apr 15 | bugfix/login

Merge into: -Users-you-dev-myrepo?
[a] all [n] none [1,2,...] pick numbers [s] skip group
>
```

### Test safely with a temp directory

```bash
PROJECTS_DIR=$(mktemp -d) bash ~/.claude/scripts/consolidate-git-history.sh --execute
```

## How It Works

The slug algorithm matches Claude Code's existing behaviour: replace every `/` and `.` in the path with `-`. The wrapper computes slugs for both the current CWD and the git root — if they differ (you're in a worktree), it creates a symlink `~/.claude/projects/<cwd-slug>` → `~/.claude/projects/<git-root-slug>` using `ln -sfn` (the `-n` flag prevents nesting inside an existing directory).

## Compatibility

- macOS (bash 3.2+) and Linux
- No dependencies beyond `git`, `python3` (ships with macOS), and optionally `fzf` for better interactive UX
- `fzf` is used automatically when available and stdin is a TTY; falls back to a numbered text menu otherwise

## Upstream Fix

The root cause is that Claude Code uses the raw CWD as the project key instead of the git repo root. The minimal upstream fix is one function in Claude Code's project-path computation:

```typescript
async function resolveProjectRoot(cwd: string): Promise<string> {
try {
const { stdout } = await execFileAsync('git', ['rev-parse', '--git-common-dir'], { cwd });
const gitCommonDir = stdout.trim();
const absCommonDir = path.isAbsolute(gitCommonDir)
? gitCommonDir
: path.join(cwd, gitCommonDir);
return path.dirname(absCommonDir);
} catch {
return cwd;
}
}
```

This plugin is the userland workaround until that change lands. Tracked in [anthropics/claude-code#52113](https://github.com/anthropics/claude-code/issues/52113).
25 changes: 25 additions & 0 deletions plugins/git-aware-history/claude-wrapper.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/usr/bin/env bash
# Git-aware Claude Code wrapper
# Installed by: https://github.com/ilanp-ob/claude-git-aware-history
#
# Disable without uninstalling:
# rm ~/.claude/git-aware-history.enabled
# Re-enable:
# touch ~/.claude/git-aware-history.enabled

if [[ -f "$HOME/.claude/git-aware-history.enabled" ]]; then
if git_common=$(git rev-parse --git-common-dir 2>/dev/null); then
[[ "$git_common" != /* ]] && git_common="$PWD/$git_common"
git_root="${git_common%/.git}"
cwd_slug="${PWD//[\/.]/-}"
root_slug="${git_root//[\/.]/-}"

if [[ "$cwd_slug" != "$root_slug" ]]; then
projects_dir="$HOME/.claude/projects"
mkdir -p "$projects_dir/$root_slug"
ln -sfn "$projects_dir/$root_slug" "$projects_dir/$cwd_slug"
fi
fi
fi

exec REAL_CLAUDE "$@"
77 changes: 77 additions & 0 deletions plugins/git-aware-history/commands/consolidate-history.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
---
allowed-tools: Bash(ls:*), Bash(which:*), Bash(grep:*), Bash(mkdir:*), Bash(chmod:*), Bash(echo:*), Bash(cat:*), Bash(sed:*), Write
description: Consolidate Claude Code session history across git worktrees into a single per-repo directory
---

# Consolidate Git History

Merge Claude Code session history from worktrees into a single per-repo directory, and install the PATH wrapper for ongoing routing.

## When invoked

Follow these steps in order. Do not skip steps.

## Step 1: Check for the consolidation script

Check whether `~/.claude/scripts/consolidate-git-history.sh` exists:

```bash
ls -la ~/.claude/scripts/consolidate-git-history.sh 2>/dev/null || echo "NOT FOUND"
```

If NOT FOUND, install it: create `~/.claude/scripts/` if needed, then write the full content of `consolidate-git-history.sh` from this plugin directory to `~/.claude/scripts/consolidate-git-history.sh` and `chmod +x` it.

## Step 2: Run dry-run and show the plan

```bash
echo "n" | ~/.claude/scripts/consolidate-git-history.sh --dry-run
```

(Pipe "n" to skip interactive prompts during the preview.) Show the full output to the user. Ask: "Does this look right? Shall I run with --execute to apply?"

## Step 3: Run with --execute (after confirmation)

Only proceed if the user confirms. Then run interactively (no piped input — the user will answer the Phase 2 prompts themselves):

```bash
~/.claude/scripts/consolidate-git-history.sh --execute
```

Show the full output.

## Step 4: Install the PATH wrapper script

This is the preferred approach — it works in every shell and IDE without sourcing `.zshrc`.

First check if the wrapper is already installed:

```bash
ls -la ~/.local/bin/claude 2>/dev/null || echo "NOT INSTALLED"
which -a claude 2>/dev/null
```

If NOT INSTALLED, install it:

1. Find the real `claude` binary (the one that is NOT `~/.local/bin/claude`):
```bash
which -a claude | grep -v "$HOME/.local/bin/claude" | head -1
```

2. Write the wrapper script, substituting the real binary path:
```bash
mkdir -p ~/.local/bin
sed "s|REAL_CLAUDE|<real-binary-path>|" <plugin-dir>/claude-wrapper.sh > ~/.local/bin/claude
chmod +x ~/.local/bin/claude
```

3. Verify `~/.local/bin` comes before the real binary on PATH:
```bash
which claude # should show ~/.local/bin/claude
```

If `which claude` still shows the old binary, `~/.local/bin` is not first on PATH. Tell the user to add this line to their shell profile and reload:
```bash
export PATH="$HOME/.local/bin:$PATH"
```

Once installed, every `claude` invocation (including `c=claude` aliases and IDE integrations) automatically routes worktree sessions to the correct git-root project directory.
Loading