Skip to content

Repository files navigation

herdr-plugin-loopreview

Pick git worktrees and GitHub PRs from a single herdr popup and review them with loopreview (lr) in a pane herdr reuses.

A keyboard-driven ratatui picker is the single entry point. It lists the repo's git worktrees immediately and streams open PRs in the background. Selecting a row opens its diff in lr, running inside one named herdr pane per tab — each new selection quits lr and relaunches it on the new target in that same pane rather than spawning more panes. A safe bulk cleanup of throwaway worktrees (the kind agents produce) is one keypress away.

Everything runs as child processes of four tools you already have: lr, gh, git, and herdr. The plugin itself has no fzf, jq, or Python dependency (the sample keybinding uses jq only to resolve the plugin's path), and the plugin never handles a GitHub token itself.

Features

  • Open / switch the live lr diff between any of the repo's worktrees, as a branch diff (origin/<base>...) or the plain working-tree diff.
  • Open a PR with lr pr <ref> — type a number, #123, owner/repo#123, or a PR URL, or pick one from the streamed list.
  • One pane per tab: selections reuse a single named loopreview pane, swapping its contents in place instead of proliferating panes.
  • Clean up worktrees safely, with multi-layer guards that refuse to delete anything still in use.

Requirements

  • lr (loopreview) — the terminal diff reviewer. Keep it current with lr update (self-updates from GitHub Releases).
  • gh — authenticated (gh auth login). All GitHub access, auth, and rate limiting go through gh; this plugin never touches a token.
  • git ≥ 2.5
  • herdr ≥ 0.7 — the terminal multiplexer this plugin runs inside.

A Rust toolchain (≥ 1.97, edition 2024) is optional — needed only to build from source. It is not required to install or run the plugin: the launcher shim downloads a prebuilt binary on first use.

Install

This plugin drives lr, so install loopreview first if you don't have it:

lr --version || curl -fsSL https://loopkeep.run/lr.sh | sh

Then install the plugin from a Git host:

herdr plugin install loopkeep/herdr-plugin-loopreview

herdr plugin install only clones the plugin — there is no build step, so it works on machines without a Rust toolchain. On first run the launcher shim downloads a prebuilt binary for your platform from GitHub Releases (via gh) and caches it under bin/.cache/.

For local development, link a checkout and build it yourself:

herdr plugin link . && cargo build --release

The shim always prefers a local target/release/herdr-loopreview if present, so a linked checkout runs your own build; otherwise it falls back to the cached or freshly downloaded release binary.

Setup

The picker is a full-screen TUI and must run inside a popup, which supplies the interactive terminal it needs and inherits the launching pane's working directory (so that becomes the target repo). Add a keybinding to your ~/.config/herdr/config.tomlthis plugin never edits your config for you:

[[keys.command]]
key = "prefix+l"
type = "popup"
description = "loopreview picker"
width = "80%"
height = "70%"
command = '''PLUGIN_DIR="$(herdr plugin list --json | jq -r '.result.plugins[] | select(.plugin_id=="loopkeep.loopreview") | .plugin_root')" && exec "$PLUGIN_DIR/bin/herdr-loopreview" picker'''

Then apply the change (or restart herdr):

herdr server reload-config

Bind a second key ending in clean instead of picker to open straight into the cleanup review.

The sample binding uses jq to resolve the plugin's install path at launch time; if you prefer not to depend on jq, hardcode the path printed by herdr plugin list as PLUGIN_DIR instead.

Usage

Inside the picker:

Key Action
Enter Open / switch the highlighted row in the default view
Ctrl-B Open the highlighted worktree in the other view (branch ↔ working tree)
Tab Toggle multi-select on a worktree (used for cleanup)
Ctrl-D Clean up the marked (or highlighted) worktree(s)
Ctrl-R Reload the worktree and PR lists
Up/Down, Ctrl-P/Ctrl-N Move the cursor
Esc / Ctrl-C Quit

Type to fuzzy-filter the list — q is a query character, so only Esc / Ctrl-C quit. Worktree matching considers the origin badge, the branch name, and the worktree's directory name (not the full path, so parent directories in an absolute path don't cause spurious matches). When the query looks like a PR reference (123, #123, owner/repo#123, or a PR URL) the picker offers a dynamic "open this PR" row.

Row badges

Each worktree row shows an origin badge and status badges:

  • Origin: herdr (~/.herdr/worktrees/…), claude (…/.claude/worktrees/…), or manual.
  • Status: * (currently shown in lr), dirty, ahead N, behind N, in use.

Command line

The picker is the default action, but the same binary can be run directly:

bin/herdr-loopreview picker    # the interactive TUI (default; needs a terminal)
bin/herdr-loopreview open      # headless: open the current worktree's diff
bin/herdr-loopreview clean     # open the picker straight into cleanup
bin/herdr-loopreview --help    # usage (also `help` / `-h`)
bin/herdr-loopreview --version # print the version (also `-V`)

The open action is headless — it opens (or reuses) the tab's loopreview pane through the plugin and needs no terminal of its own, so it works from scripts and agents. It prints a one-line result and exits non-zero on failure:

bin/herdr-loopreview open                  # branch diff of the current worktree (default view)
bin/herdr-loopreview open --view worktree  # the plain working-tree diff instead
bin/herdr-loopreview open --pr 123         # a PR (number, #123, owner/repo#123, or URL)
bin/herdr-loopreview open --path <dir>     # the worktree containing <dir>, not cwd

picker and clean need an interactive terminal; open, help, and --version run anywhere.

Configuration

Optional settings live in config.toml inside the plugin config directory (herdr plugin config-dir loopkeep.loopreview). The plugin only ever reads this file:

# Fallback base branch when origin/HEAD cannot be detected.
# Default: main
# base_branch = "main"

# Launch lr watching for changes (auto-reload). Default: true.
# false adds --no-watch.
# watch = false

# Layout mode passed to `lr --mode`. Only "unified" or "split" are honored;
# "auto" or any other value is ignored (lr's own default is used).
# mode = "split"

# Split ratio (0.0–1.0) for the spawned loopreview pane. Default: herdr's split.
# pane_ratio = 0.6

# Which view Enter opens a worktree as: "branch" (default) or "worktree".
# Ctrl-B always opens the other one.
# default_view = "branch"

# Where a fresh loopreview pane opens: "rightmost" (default) splits the
# tab's rightmost pane; "current" splits the pane the popup was opened from.
# pane_placement = "rightmost"

# The lr executable to run (name on PATH or an absolute path). Default: lr.
# lr_bin = "lr"

# How many PRs to request from `gh pr list`. Default: 50.
# pr_limit = 50

Per-tab pane tracking is recorded in state.json in the same directory.

How it works

Target repository

The picker reviews the repository at the popup's working directory ($PWD). If that is not a git repository it reports an error — open the picker from a pane inside the repo you want to review.

One loopreview pane per tab

The plugin keeps exactly one named loopreview pane per herdr tab and swaps its contents rather than proliferating panes. Selecting a target:

  1. If the pane already shows that exact target and lr is still running, it just focuses the tab.
  2. Otherwise it reuses the pane in place: if lr is running it sends q and waits (up to ~3s) for the shell to return, then cds into the target and relaunches lr. An idle shell is reused directly (no q is sent). A pane running some other program is never disturbed.
  3. If in-place reuse is not safe — the pane is gone, process inspection fails, the quit times out, an unknown program is running, or the target path can't be safely quoted — it closes that pane (if any) and splits a fresh one.

A fresh pane opens according to pane_placement: the default rightmost splits the tab's rightmost pane so it lands at the far-right edge of the tab (falling back to the current pane when the tab's geometry can't be read), while current splits the pane the popup was opened from.

A branch-diff open resolves its diff base in this order: origin/<base> (the worktree's origin/<base> is fetched first, best-effort — an offline failure just notes the comparison may be stale), else the local <base> ref (only when the worktree isn't already on <base>, since comparing a branch against itself is an empty diff), else it falls back to the working-tree view (plain lr, with a note that no usable base ref was found). Worktree opens run lr in the worktree's directory; PR opens run lr pr <ref> in the repo root.

Cleanup safety

Cleanup never happens automatically. Ctrl-D opens a confirmation modal that shows each worktree's assessment (dirty / unpushed / clean) and any reason it is considered in use. A worktree is in use — and cannot be removed even with force — when any of these hold:

  1. It is open in a herdr workspace (herdr worktree list).
  2. A herdr pane's cwd/foreground_cwd is inside it (herdr pane list).
  3. A running process has its working directory inside it (lsof, best-effort).
  4. A live loopreview session is currently showing it.

Dirty or unpushed worktrees are refused by default; press f in the modal to enable force for those (in-use is still refused). On removal the plugin runs git worktree remove, then deletes the local branch only when it is provably safe (has an upstream and is not ahead, or is merged into the default branch) — the remote is never touched. The main worktree is never offered for cleanup.

Agent integration

Every lr this plugin launches hosts loopreview's control plane: a local per-session socket that agents and scripts can drive with lr session * (inspect the diff and threads, move the reviewer's view, leave draft comments, wait for replies) — the TUI stays the human's. This pairs naturally with reviewing agent worktrees: the agent that produced the change can steer the very review you opened with the picker.

An agent running inside herdr (HERDR_ENV=1) can go one step further and open the review itself, in its own tab, instead of waiting for a human to run the picker: the headless herdr-loopreview open action points the tab's loopreview pane at the agent's worktree, branch, or PR (see Command line). Install the herdr-loopreview skill to teach an agent that:

npx skills add loopkeep/skills -s herdr-loopreview

To steer the review once it is open — inspect the diff and threads, move the reviewer's view, leave draft comments, wait for replies — install the loopreview-session skill from loopkeep/skills, the home of the loopkeep family's agent skills:

npx skills add loopkeep/skills -s loopreview-session

The two compose: herdr-loopreview opens the pane, loopreview-session drives the review.

Actions

Action What it does
loopkeep.loopreview.picker The interactive TUI (default)
loopkeep.loopreview.clean Open the picker straight into the cleanup review

License

MIT

About

herdr plugin for loopreview

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages