Skip to content
gwmgwmgwmv1.10.0

herdr (plugin)

herdr is a terminal multiplexer with workspaces, panes and a plugin API. 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, with 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.

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.

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.

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

Terminal window
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.

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

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

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):

# "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.

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.