gwm-cli 1.6.1

git worktree manager — TUI + CLI, native libgit2, per-repo bootstrap
Documentation
---
title: herdr (plugin)
description: Drive gwm from inside the herdr multiplexer — the actions shipped by herdr-plugin-gwm, its adopt-only rule, install and configuration.
---

# herdr (plugin)

[herdr](https://herdr.dev) is a terminal multiplexer with workspaces, panes and a plugin API. [`herdr-plugin-gwm`](https://github.com/kbrdn1/herdr-plugin-gwm) wires gwm into it: create, switch, remove, review a PR, exec and clean across worktrees, and gwm's own TUI in a pane — each worktree reflected as a herdr workspace.

The plugin is glue-only bash. It holds no worktree logic of its own: every script is `gwm <cmd> --format=json` → `jq` → a herdr CLI call, riding the frozen 1.0 JSON contract of [`list`, `path` and `doctor`](/cli/reference).

## the one rule

> The plugin never calls `herdr worktree create` or `worktree open --branch`. Creation and removal always go through gwm; herdr only reflects, via `worktree open --path` (adopt) and `workspace close` / `workspace focus`.

That is what keeps a single source of truth. Break it and herdr starts creating git worktrees outside gwm's control — two writers on the same repo, drifting apart on the first `gwm remove`. The plugin enforces it rather than documenting it: one helper (`adopt_worktree`) is the only path to herdr, and a grep-assert in its test suite fails the build if any script reaches around it.

## install

| Channel | Command |
|---|---|
| herdr marketplace | `herdr plugin install kbrdn1/herdr-plugin-gwm` |
| Local checkout | `herdr plugin link "$PWD"` |

There is no build step — plain bash, nothing to compile.

**Requirements:** herdr ≥ 0.7.4 (popup panes) · `gwm` on `PATH` · `jq` · `fzf` · bash · macOS or Linux.

## the actions

Invoke from any pane inside a herdr workspace sitting in a gwm-managed repo:

```bash
herdr plugin action invoke gwm.switch
```

| Action | What it does | Opens as |
|---|---|---|
| `gwm.create` | branch type → issue number → description → `gwm create`, then adopts the new worktree, resolved by its linked issue | split pane |
| `gwm.switch` | fzf over `gwm list --format=json` with live badges (issue `#N`, `PR#N`, dirty `±`, ahead ``, behind ``) — focuses the workspace if herdr already reflects the pick, adopts otherwise | popup |
| `gwm.remove` | fzf over removable worktrees (never the main checkout) → confirm gate → `gwm remove` (branch kept) → `workspace close` | popup |
| `gwm.review` | fzf over `gh pr list``gwm review <N>` materialises the PR as its own worktree → adopt | split pane |
| `gwm.exec` | `gwm exec -- <cmd>` across every worktree, with a `✓/✗` rollup | split pane |
| `gwm.clean` | `gwm clean` reports reclaimable build artifacts, deletes after a confirm | split pane |
| `gwm.dashboard` | gwm's TUI, as-is | zoomed pane |

Pick-and-go pickers are session-modal popups so the tiled layout does not move; the panes whose *output* is the point (`exec`, `clean`) or that run long git or network work (`create`, `review`) stay split — a modal that blocks the session for a 30-second clone reads as frozen.

Two things happen without being invoked:

- **A clicked GitHub PR URL** (`https://github.com/<owner>/<repo>/pull/<N>`) triggers `gwm.review`. The pattern is anchored and the script re-extracts the number itself, so only an integer ever reaches gwm — never a raw URL.
- **`worktree.created`**, fired when a worktree is created on the herdr side, outside gwm, runs `gwm bootstrap` on its path so it gets the same file copies, hooks and preset as a `gwm create` one. Adopts fire `worktree.opened`, not `.created`, so this never double-runs on the plugin's own work.

## binding a key

In `~/.config/herdr/config.toml`, then `herdr server reload-config`:

```toml
[[keys.command]]
key = "prefix+ctrl+shift+g"
type = "plugin_action"
command = "gwm.switch"
description = "gwm: switch worktree"
```

## configuration

Presentation only — the plugin has no behaviour to configure, since gwm owns the behaviour. Create `~/.config/herdr/plugins/config/gwm/config.toml` (or run `herdr plugin config-dir gwm`):

```toml
# "workspace" (default) → adopt as a nested worktree workspace in the sidebar.
# "tab"                 → lighter: open a tab with the worktree cwd.
open_mode = "workspace"

# "user" (default) → inherit your FZF_DEFAULT_OPTS (colors, borders); the picker
#                    only neutralizes file-browser bits that would garble
#                    non-file lines or rebind keys.
# "clean"          → drop FZF_DEFAULT_OPTS entirely for a bare picker.
fzf_theme = "user"
```

Worktrees are adopted under the repo's *root* workspace, so invoking an action from inside a linked-worktree pane does not hit herdr's `linked_worktree_source` rejection.

## limits

Multi-repo mode (`gwm --workspace`) is not wired through the actions yet: the plugin operates on the single repo of the current workspace. Everything else — create, switch, remove, review, exec, clean, dashboard, bootstrap-on-create — is implemented.