herdr is a terminal workspace manager for AI coding agents (0.8.0 at time of writing). It has spaces, tabs, and panes, but no session-per-project layer the way tmux does, so "switch to project X" isn't a built-in. This is that binding.
Hit ctrl+a f, get an fzf popup listing every directory under ~/work/src and
~/projects, pick one, and land in a herdr space labelled with the project's
basename whose first tab starts in that directory. If a space with that label is
already open, it focuses that one instead of creating a second.
project> rails
work: brainarray
work: rails-upgrade-notes
> proj: railsconf-talk
proj: dotfiles
| File | Where it goes |
|---|---|
herdr-project |
anywhere on PATH (~/bin/herdr-project), chmod +x |
config.toml |
the [[keys.command]] block goes in ~/.config/herdr/config.toml |
tmux-original.conf |
the tmux binding this was ported from, for reference |
Requires herdr, fzf, and jq.
install -m 755 herdr-project ~/bin/herdr-project
# paste the [[keys.command]] block into ~/.config/herdr/config.toml
herdr config check && herdr server reload-configEdit WORK_ROOT and PROJ_ROOT at the top of the script to match wherever your
projects actually live. Two roots is arbitrary; add or drop cases in list_projects
and the case statement below it in matching pairs.
The [[keys.command]] array-of-tables has to be last in [keys]. TOML scoping,
not a herdr quirk: any plain key = value line placed after it parses as part of
keys.command rather than keys. herdr config check catches it, and the error
points at the wrong line.
Bare width/height numbers are terminal cells, chosen to match tmux's
display-popup -w 60 -h 20. If you want a percentage it has to be a quoted string,
width = "80%".
fzf exits 130 when you press Esc. Under set -e that turns "never mind" into a
failed command and an error in the popup, so the pick is written as
choice=$(...) || exit 0.
The workspace lookup avoids first/1. [.result.workspaces[] | select(...)] | .[0] // empty
does the same job as first(...) and works on jq builds that don't implement it.
Labels are directory basenames, so two projects sharing a basename across the two
roots also share a space. The work: / proj: prefixes exist only to disambiguate the
fzf list; they're stripped before the path is rebuilt.
The original binding did the same pick inline in tmux.conf and ended in
new-session -d -s "$name" -c "$path" + switch-client. That works because a tmux
session is a first-class named thing you can create detached and jump to. herdr's
equivalent target is a space, created with
herdr workspace create --cwd <path> --label <basename> --focus, and there's no
"create detached, switch separately" split to worry about. The whole thing outgrew a
quoted one-liner in a config file, hence the script.