A colorful herdr command palette with three searchable pickers: Projects for agents/workspaces/ghq repos/worktrees, Commands for exact shell history and presets, and Ports for live local TCP listeners. Open the central menu or bind each picker directly.
Where ghq list | fzf | cd can only change a directory, this uses herdr as a multiplexer:
jump to a live agent, switch workspaces, or open a repo/worktree exactly where you want it —
a new workspace, tab, split, or the current pane. It is a Rust TUI (ratatui + nucleo); no
fzf required.
Warning
This plugin is early, in active development, and experimental. It is pre-1.0
(0.x): behaviour, keybindings, configuration keys, and the on-disk state format can
change between releases, sometimes without a migration path. It also tracks herdr's own
fast-moving CLI/socket API and may break as herdr evolves. Expect rough edges,
pin a version if you need stability, and please
report issues. The one destructive
destructive actions always require typed confirmation: repository removal asks for
the repo name, while Port TERM/KILL asks for the signal word and revalidates process identity.
| herdr ≥ 0.7.4 | the host multiplexer |
ghq |
repository source |
fallback Rust / cargo |
only needed if a release binary cannot be downloaded |
tuicr ≥ 0.20.0 |
the git menu's review tool (brew install tuicr) |
optional gh |
the git menu's pull-request row (hidden without it) |
optional lazygit |
staging/commit from the git menu |
optional eza |
richer preview tree |
herdr plugin install crafts69guy/herdr-switchboardBind a key in ~/.config/herdr/config.toml (see examples/keybindings.toml):
[[keys.command]]
key = "prefix+space"
type = "plugin_action"
command = "switchboard.menu"
description = "Switchboard menu"Reload, then press prefix+space:
herdr server reload-configThe picker works like a Telescope/LazyVim picker: it opens typing (Insert mode), and
esc drops to Normal mode for Vim motions. A NORMAL / INSERT tag on the search box
says which mode owns the keys, ? shows the live cheatsheet for that mode, and the
command bar re-labels itself per mode. Press i or / in Normal to type again.
Accept (enter) is kind-aware in either mode:
| Highlighted | enter |
|---|---|
| agent | jump to it (herdr agent focus) |
| workspace | switch to it (herdr workspace focus) |
| repo | open it in default_target — a new workspace unless configured otherwise |
| worktree | open its linked checkout in default_target |
Insert mode (type to filter; esc → Normal, ^c closes):
| Key | Does |
|---|---|
↵ · ⌥↵ |
open · switch to the clone flow |
^j/^n · ^k/^p |
down · up |
^t · ^v · ^o |
open in a new tab · split · the current pane (cd) |
⌥w · ^r · ^x |
to a workspace · ghq get -u · remove |
tab / ⇧tab |
cycle groups (All → Agents → Workspaces → Repos → Worktrees) |
⌥p · ⌥s · ⌥j/⌥k |
toggle preview · cycle sort · scroll the preview |
^u · ^w · ⌫ |
clear the query · delete a word · delete a char (readline) |
⌥, · ⌥c · ⌥u · ? |
settings · changelog · update the plugin itself · this cheatsheet |
Normal mode (esc from Insert; i or / returns): bare h/j/k/l motion is Vim's —
j/k move, g/G top/bottom, ^d/^u page, H/L prev/next group. Frequent opens sit
on unshifted keys — t tab, v split, o cd, w workspace, p toggle preview — and the
␣ leader groups the rest: ␣u update repo, ␣x remove, ␣c clone, ␣s sort,
␣l changelog, ␣, settings. q or esc closes. (? always shows the exact, current bindings.)
Anywhere: the wheel scrolls the pane under the pointer (card over the preview, list elsewhere); a click selects an entry, filters on a group tab, or runs a command-bar pill.
Sorting defaults to recent, so repos you opened last float to the top; opens are recorded
in ${XDG_STATE_HOME:-~/.local/state}/herdr-switchboard/recent.tsv. While you type, fuzzy score
orders the list — sort only applies to the resting, no-query list.
Remapping. Every binding is a chord → action entry you can change in config.toml:
[keys.projects]
tab = "ctrl-y" # cycle groups on ^y instead of Tab
split = "ctrl-x" # split on ^x instead of ^v
down = "ctrl-j,ctrl-n" # one action, several chordsA chord is a key with optional ctrl- / alt- / shift- prefixes. The full list of action
names is in examples/config.toml, and the footer + ? cheatsheet
re-render from your bindings, so they always show what you actually set.
Start mode. keymode = "normal" opens the picker in Normal mode (Vim-first) instead of
Insert. Normal mode is always one esc away either way.
Bind any of these as a Herdr plugin_action:
| Action | Does |
|---|---|
switchboard.menu |
searchable central menu |
switchboard.projects |
Projects: agents, workspaces, repos and worktrees |
switchboard.commands |
Commands: shell history and configured presets |
switchboard.ports |
Ports: live listeners, owner process and safe actions |
switchboard.settings |
package settings (Common / Projects / Commands / Ports) |
switchboard.git |
the git menu for the current repo, in its own pane (bind to prefix+g) |
switchboard.clone |
the clone flow |
switchboard.changelog |
what changed, with your installed version marked |
switchboard.update |
install a newer version (refuses to touch a linked checkout) |
switchboard.open-workspace · switchboard.open-tab · switchboard.open-split |
the switcher with enter's repo target forced |
Commands merges zsh, Bash, or fish history with [commands].presets, deduplicating by the exact
command text. It excludes common credential patterns before persistence; add Rust regexes through
history_exclude. Forgotten commands are fingerprinted in a denylist so the next shell import does
not bring them back. alt-s cycles frecency/recent/frequency/alphabetical ordering. Enter fills the origin pane, ctrl-enter runs there, alt-enter runs from the
most recent historical cwd, ctrl-y copies, and ctrl-x forgets. Multiline execution requires a
typed confirmation and the full command is never included in notifications.
Ports refreshes native TCP listener data off the input loop and groups IPv4/IPv6 addresses for the
same PID and port. Search fields include port:, address:, pid:, process:/proc:, cwd:,
repo: and user:; quoted values and negated filters are supported. Enter copies
localhost:PORT, ctrl-enter/alt-enter open HTTP/HTTPS, and ctrl-w opens the process cwd as a
workspace. TERM (ctrl-x) and KILL (alt-x) each require confirmation, ownership, and a fresh
PID + process-start identity check; Switchboard never signals a parent or process group.
The git menu is its own herdr pane, opened by the switchboard.git action — bind it to prefix+g.
It acts on the repo the pane you launched from is sitting in, and it loads nothing else: no
agents, no workspaces, no repository index, no preview. That is the whole point of it being a
separate pane rather than an overlay on the switcher.
There is no
⌥gin the switcher any more. Reviewing an arbitrary repo you fuzzy-jumped to is gone with it;cdthere (^o) and pressprefix+g.
Walk it with ↑/↓, enter runs the row, a mnemonic letter runs it directly, esc closes.
Whatever you pick takes over this pane — quit the tool and you are back where you started.
| Key | Row | Runs |
|---|---|---|
d |
review worktree | tuicr -w — staged and unstaged together |
b |
review branch | tuicr -r <base>.. -w — base auto-detected, or pinned via base_branch |
h |
review commits | tuicr — tuicr's own commit selector |
a |
review all files | tuicr -A |
p |
review pull request | gh pr list → tuicr pr <n> (hidden when gh is not installed) |
r |
saved reviews | tuicr review list → tuicr review comments --session <slug> |
l |
lazygit | stage / commit / push (shown only when lazygit is installed) |
p and r open a sub-list: type to fuzzy-filter, ↑/↓ (or ^n/^p) move, enter picks,
and esc clears the filter before backing out to the menu.
r reads comments, it does not reopen a review. --session exists only on tuicr's headless
review add / review comments; the TUI does not take it.
Reviews open in tuicr (brew install tuicr, ≥ 0.20.0), which
is a hard requirement — the menu is a review menu. Its colours come from tuicr's own
theme setting, which hue-theme generates; this
plugin writes nothing into tuicr's config, because a theme it half-agreed with would make tuicr
exit 2 and take every review down with it.
Add your own rows in menu.conf (key|icon|label|shell command) beside config.toml.
Settings live in namespaced TOML in the plugin config dir (herdr plugin config-dir switchboard).
Edit it directly, copy examples/config.toml, use
switchboard.settings, or press ⌥, in Projects. Tab/shift-tab switches
Common / Projects / Commands / Ports; ↑/↓ moves and enter changes a value. Edits are
drafts: a ● marks each changed row, a applies them all to config.toml, and esc discards
them. Applying takes effect in the running switcher — the list re-sorts, sources and preview
reload, colours and key rebinds update on the spot; no relaunch or server reload needed.
Every key is documented in examples/config.toml. The ones you're most likely to want:
| Key | Values |
|---|---|
default_target |
workspace (default) · tab · split · pane |
default_tab |
all (default) · agents · workspaces · repos · worktrees |
include_agents / include_workspaces |
blend agents/workspaces into the list |
include_worktrees |
list linked Git worktrees (true by default) |
sort |
recent (default) · name · kind |
keymode |
start mode: insert (default) · normal (Vim-first) |
[keys.projects] / [keys.commands] / [keys.ports] |
picker-specific keymaps |
label |
workspace/tab label: repo · owner-repo · path |
preview / preview_readme |
the preview pane |
clone_source |
seed the clone prompt from the clipboard (default) or start blank |
base_branch |
base for the git menu's branch review (blank = auto-detect) |
split_direction / split_ratio |
geometry for split targets |
update_check |
ask GitHub once a day whether a newer version is tagged (true by default) |
notifications / notification_position |
herdr toasts, and which corner they land in |
notification_sound |
auto (per-event, default) · none · done · request |
The switcher is themed from herdr's [theme.custom], and previews each kind as a card —
a header with the entry's state as a pill, aligned label value rows, then bodies under
captioned rules. Repos and worktrees show branch · clean/dirty · last commit, a file tree,
and a README excerpt rendered as markdown; agents show what they are doing and their recent
output, in the agent's own colours; workspaces list their tabs, each with its live status.
Long cards scroll with alt-j / alt-k.
Switchboard only reads its namespaced configuration from the switchboard plugin config directory.
Bind switchboard.projects for the direct Projects picker or switchboard.menu for the Central
Menu.
update_check only ever shows ↑ v0.6.0 in the command bar — it never installs anything.
Set it to false to disable that daily check. A managed install can still contact GitHub once
to fetch its version-matched, checksummed switcher binary; linked development checkouts build
their local source instead.
Each action starts in bin/action.sh, which captures the origin pane id and cwd before the
new pane steals focus, then opens that pane — an overlay for the picker, the git menu, and the
clone flow, a popup for the changelog.
The picker itself is the Rust TUI in src/. On a managed install, bin/picker.sh selects a
versioned macOS/Linux release binary for the host architecture and verifies its SHA-256;
offline or linked checkouts fall back to Cargo. A small typing-cat bootstrap animates during
that one-time preparation.
The TUI then reads herdr agent list, herdr workspace list, ghq list, and Git's stable
worktree list --porcelain -z output synchronously, before it claims the terminal — the
whole set costs around 35 ms, so the first thing painted is the loaded list rather than a
placeholder. (An empty result never claims the terminal at all: it hands the pane straight to
the clone flow.) It fuzzy-filters with nucleo and previews the selection as a card drawn in
your herdr theme colours — bin/preview.sh supplies only the repo/worktree file tree. On
accept it maps the key to a herdr CLI verb — agent focus, workspace focus,
workspace create, tab create, pane split, pane send-text — always targeting the
captured origin pane or a real id from herdr, never a guessed one.
The git menu is the same binary in --git mode, in a pane of its own, and picking a row
execs bin/review.sh over it. One static ASCII-cat frame is drawn first and left on screen
for tuicr to paint over, because tuicr can take a second or more to read a large diff before
its own first frame. The changelog viewer is --changelog mode; the settings form is a
floating overlay inside the switcher itself (⌥,), not a separate pane. Only the clone flow
is still bash (bin/get.sh).
Set SWITCHBOARD_TRACE=1 and every launch appends tab-separated timings to
$XDG_STATE_HOME/herdr-switchboard/trace.log (or $SWITCHBOARD_TRACE_FILE). Nothing is ever written to
stdout or stderr — the TUI owns the terminal for its whole life.
SWITCHBOARD_TRACE=1 herdr plugin action invoke projects --plugin switchboard
awk -F'\t' '$2 == "frame.first_list" { print $1 }' ~/.local/state/herdr-switchboard/trace.log
awk -F'\t' '$2 == "preview.render" { n++; t += $3 } END { print t / n }' \
~/.local/state/herdr-switchboard/trace.logThe budgets the current code is held to: first list < 100 ms, keystroke-to-frame < 16 ms, preview render < 50 ms mean and < 70 ms at the tail.
Issues and pull requests are welcome. Start here:
git clone https://github.com/crafts69guy/herdr-switchboard
cd herdr-switchboard
herdr plugin link "$PWD" # install this checkout
herdr server reload-configBefore you open a PR:
cargo fmt --check
cargo clippy --all-targets -- -D warnings
cargo test
bash tests/manifest_spec.sh # manifest contract, version sync, bash syntax
bash tests/update_guard_spec.sh # the update guard (herdr is stubbed, never called for real)
bash tests/bootstrap_spec.sh # target mapping, release checksum, atomic installCI runs those checks on every change. Tagged releases additionally build four native binaries (macOS/Linux, arm64/x86_64) and publish them only after every target succeeds. Two things are easy to miss:
- Any user-visible change adds a line to
CHANGELOG.md's[Unreleased]section in the same commit —bin/release.shpromotes that section and the release workflow publishes it verbatim as the GitHub release notes; nothing is generated fromgit log. - Don't bump versions by hand.
Cargo.tomlandherdr-plugin.tomlmust match, andbin/release.shbumps both.
Layout, keybinding, and herdr CLI changes need manual exercise in a real herdr session —
there is no test runner for the overlay. Please attach a screenshot when visual output
changes, and test ctrl-x against disposable repos.
AGENTS.md has the full conventions: module layout, coding style, testing, and
the safety rules around herdr ids and destructive flows.
Run switchboard.changelog to read it in a popup with your installed version marked, or see
CHANGELOG.md. Releases are tagged vX.Y.Z; to update, re-run the install
command (it re-fetches the ref) or use the switchboard.update action. Watch the repository
(Watch → Custom → Releases) to hear about new versions.
MIT — see LICENSE.
