Skip to main content
Glama
README.md
# pleach

Branch tracking, visualization, and merging for Claude Code sessions.

Claude Code can fork a conversation (`/branch`, `--fork-session`) but keeps no
record of what forked from what, shows no tree anywhere, and has no way to bring
two divergent branches back together. `pleach` adds those three things.

> Unofficial third-party tool. Not affiliated with or endorsed by Anthropic.
> It reads session transcripts already on your disk and shells out to your own
> `claude` binary. It never touches credentials and never bundles Claude Code
> source.

```
* 9703da58 (leaky-bucket) 2026-08-01
| * a7aa4a1d (sliding-window) 2026-08-01
|/
| * 36a05f40 (token-bucket) 2026-08-01
|/
* 97c6581b 2026-08-01
```

If you can read `git log --graph`, you already know how to read this.

## Requirements

Python 3.9+ and an installed `claude`. No dependencies, no build step, no
`pip install` — it runs from a checkout. (Claude Code 2.x ships as a native
binary and brings no Node runtime, which is why this is Python.)

## Learn it in five minutes

Three commands, modeled on git:

```bash
pleach log              # the session graph for this project — works instantly,
                        # even on history from before you installed pleach
pleach branch 9703da58  # fork a session; prints the `claude --resume` command
pleach merge a7aa a1d3  # reconcile two branches via a background agent
```

That's the whole core. Ids take any unambiguous prefix, like git SHAs. Typing
bare `pleach` prints this cheatsheet.

Two optional extras that pay for themselves:

```bash
pleach name 9703da58 leaky-bucket   # labels show in the graph, like branch names
pleach init --write                 # install hooks: new forks tracked live
                                    # (backs up settings.json; --uninstall reverses)
```

There is no index to build and no scan step — every command refreshes lineage
for the current project automatically, the way git just reads your repo.

## The interactive graph

```bash
pleach ui
```

Full-screen interactive graph in the spirit of VS Code's git-graph panel, keys
modeled on tig/lazygit: `j/k` move between nodes, `enter` toggles the detail
panel, `n` names, `b` branches, `s` opens a branch summary in a pager, `m`
marks a node and `m` on a second node merges the pair (dry-run or dispatch),
`r` prints the resume command, `a` toggles all projects, `q` quits.

The VS Code extension takes the same keys, so the two surfaces are one set of
muscle memory: `j/k` (or arrows) move, `g/G` jump to the ends, `enter` resumes,
`b` branches, `n` renames, `s` summarizes, `o` opens the transcript, `m` pins a
session as the other half of a merge, `a` toggles all projects, `/` focuses the
filter, and `?` lists the lot.

Claude Code's TUI has no extension point for custom panes, so `ui` can't render
*inside* a session — run it in a terminal pane next to one, exactly how the VS
Code git graph sits next to the editor. Inside a session, `/pleach` (from the
plugin) is the same functionality with Claude as the interaction layer: it
shows the graph and resolves "branch the top one as X" or "merge those two"
into the right commands for you.

## Commands

| Command | What it does |
|---|---|
| `ui` | Interactive full-screen graph — navigate, name, branch, merge. |
| `log` (alias `tree`) | Git-style session graph. `--all` for every project. |
| `branch <id>` | Fork a session (a file copy — no API call). |
| `merge <a> <b>` | Reconcile two branches via a background agent. `--dry-run` first. |
| `name <id> <label>` | Label a session; labels show in the graph. |
| `summary <id>` | What happened in a branch, distilled. Cached. |
| `grep <term>` | Full-text search across transcripts. `--all` for every project. |
| `export <id>` | Render a transcript as readable markdown. Local, free. |
| `show <id>` | Parent, branch point, children, entry count. |
| `merges` | Merges run here, and whether the agent has finished. |
| `status` | What's tracked, what's live, what's configured. |
| `list` | Flat table of tracked sessions. |
| `scan` | Full rescan of every project (per-project refresh is automatic). |
| `prune` | Drop stale index entries; `--snapshots` also deletes merge bases. |
| `doctor` | Environment checks; `--test-synthesis` verifies merge-base support. |
| `mcp` | Run the MCP server (stdio) — see below. |

## Give Claude itself the graph (MCP)

`pleach mcp` is a zero-dependency [MCP](https://modelcontextprotocol.io) server
over stdio. Register it once:

```bash
claude mcp add --scope user pleach -- python3 /path/to/checkout/bin/pleach mcp
```

From then on, Claude — in any terminal session *and* inside the official VS
Code extension's panel — can query your session graph directly. Ask it "which
of my branches dealt with the flux bug?" and it can call `pleach_grep`, read
the graph with `pleach_log`, pull a branch digest with `pleach_summary`, or
hand you the exact `claude --resume` command via `pleach_show`.

Tools exposed: `pleach_log`, `pleach_show`, `pleach_grep`, `pleach_export`,
`pleach_summary`, `pleach_branch`, `pleach_name`, `pleach_merge_plan`,
`pleach_merge`, `pleach_status`. Reads are free and local (summaries cost a
cached haiku call). The only side-effectful tools are `pleach_branch` (copies
a transcript, same as `pleach branch`) and `pleach_merge` (dispatches a
background merge agent); `pleach_merge_plan` is the dry run and its tool
description tells the model to use it first.

Remove with `claude mcp remove pleach`.

## How merging works

A merge is not a replay of tool calls. It is:

1. **Reconstruct the shared context.** Find the last entry both branches share —
   their branch point — and rebuild the conversation as it stood there.
2. **Summarize each branch separately.** A cheap model reads each full branch
   and reports what was tried, what worked, what failed, what was decided.
3. **Seed a new agent from the shared context** and hand it both summaries to
   reconcile.

Step 3 runs as a background session (`claude agents` to watch it), so it doesn't
disturb the session you're in. The merge agent is **read-only by default**: it
is given no tools at all, since it is pure synthesis over two summaries already
in its prompt. Pass `--allow-writes` if you actually want it editing files.

Merge bases are reused when the branch point hasn't moved, so re-merging the
same pair doesn't accumulate transcripts. `pleach prune --snapshots --yes`
removes the ones pleach created.

Use `--dry-run` to see the merge base, its confidence, and the exact prompt
without dispatching anything.

Because the agent runs in the background, `pleach merges` says which merges
have finished, and `pleach export <merge-session>` reads the reconciliation.
The extension does both for you: the sidebar tracks a dispatched merge from
"running" to "ready" and offers a **Read the merge** button.

## What's verified

Built against measured behaviour, not assumptions. The experiments and results
are in [PLAN.md](PLAN.md) §8, and the whole pipeline has been run end to end
against real sessions:

- **Fork copying (§8.2).** Forks preserve entry uuids exactly while rewriting
  the `sessionId` field — the property that makes retroactive inference work.
- **Inference (§8.9).** 19 edges inferred from real history, each re-derived
  independently from the raw transcripts: 19/19 correct parent and exact
  branch point.
- **Live fork detection (§8.10).** Confirmed against a real fork with hooks
  installed.
- **Merge-base synthesis (§8.6).** Confirmed: Claude Code resumes a transcript
  pleach wrote. Merge bases are exact, and `pleach branch` costs no API calls.
- **The merge pipeline.** Dispatched, ran, and produced a real reconciliation
  seeded from the correct shared context.

`pleach doctor --test-synthesis` re-checks the synthesis property on your
machine. If a future release breaks it, merges fall back to forking the live
parent and say so with a `drifted` warning rather than silently using a wrong
base.

**Still untested:** the interactive `/branch` and `/fork` TUI commands. All fork
testing used the `--fork-session` CLI flag. `scan` catches those either way.

## What it refuses to do

The lineage index is the only place fork edges, names, and merge history live —
none of it can be rebuilt by rescanning transcripts. So pleach never replaces a
file it could not read:

- An unreadable `~/.pleach/lineage.json` is recovered from `lineage.json.bak`
  (written before every change) or, failing that, left exactly as it is while
  the command exits **3**. It is never overwritten with an empty index.
- An unreadable `~/.claude/settings.json` stops `pleach init` entirely, with
  the same exit code. `--write` and `--uninstall` both back it up first, and
  `--uninstall` leaves the file alone if none of pleach's hooks are in it.
  (A UTF-8 BOM counts as readable — it used to count as "empty".)

Transcript text is untrusted input: it is whatever a session contained,
including anything an agent quoted from a web page. Text sent to a model is
fenced with a per-call nonce and the rules are restated after the content, so a
transcript that contains instructions gets *reported* rather than obeyed —
verified end to end against a deliberately poisoned transcript. Merge agents
still run with no tools unless you pass `--allow-writes`.

## How branch detection works

Claude Code has no fork hook, and it signals forks **differently across
versions** — pleach handles both.

On recent versions (measured on 2.1.219), `SessionStart` announces the fork
explicitly with `source: "fork"`, but reports the **child** id and never names
the parent. The parent is recovered by prefix-matching the child's transcript
against its siblings, which is exact because a fork's transcript literally
begins with its parent's.

On older versions (2.1.207), `SessionStart` instead reports the **parent** with
`source: "resume"` — indistinguishable from a plain resume — and the child only
appears on `UserPromptSubmit`. There the signal is the mismatch between the two
events.

This is why `init` installs three hooks rather than one: on both paths a single
`SessionStart` hook detects nothing. Details in `pleach/track.py`.

One consequence worth knowing: the child's transcript is not on disk when
`SessionStart` fires, so parent resolution is retried on later events and falls
back to `scan`. A missed hook degrades to inference, never to a lost branch.

Edges found live are marked `observed`. Edges reconstructed by `scan` are marked
`inferred` — those pick the closest plausible ancestor, so in rare cases (a
branch taken immediately, before the parent did further work) they may attribute
to a grandparent rather than a parent. A shallower tree, not a wrong one.

## Layout

```
bin/pleach            entry point
pleach/track.py       hook handler — the fork-detection state machine
pleach/infer.py       retroactive lineage inference
pleach/transcript.py  JSONL parsing, prefix comparison, branch points
pleach/mergebase.py   shared-context reconstruction
pleach/merge.py       the merge pipeline
pleach/graph.py       tree building and rendering
test/smoke.py         offline end-to-end tests
```

Run the tests with `python3 test/smoke.py`. They use a throwaway config dir and
never touch your real `~/.claude`.

## Storage

Everything pleach records lives in `~/.pleach/` (override with `PLEACH_HOME`).
The only thing it ever writes into `~/.claude/projects/` is a reconstructed
merge base, which is tagged and removable with `pleach prune --snapshots --yes`.
It never modifies an existing transcript.

## License

MIT.

TDQS

A3.9/5.0

Scored across 10 tools

Disambiguation5/5

Each tool targets a distinct aspect of session branching: graph overview, session details, search, export, summary, forking, labeling, merge planning, merging, and tracking stats. There is no meaningful overlap between any two tools.

Naming Consistency4/5

All tools share the consistent 'pleach_' prefix and snake_case, but the second part mixes verbs (show, grep, export, branch, name, merge) and nouns (log, summary, status, merge_plan). This is mostly consistent with minor deviations.

Tool Count5/5

Ten tools is well-scoped for a session branching system, covering the full range of operations without redundancy or bloat. Each tool earns its place in the set.

Completeness4/5

The tool surface covers core operations: branch, merge (with planning), inspect (log, show, grep, export, summary), label, and status. A minor gap is no delete/archive operation for sessions, but this is likely intentional and does not block primary workflows.

Maintenance

ActivityMaintained
ResponsivenessSyncing