Configuration¶
herdr-sesh reads a versioned native TOML config. Every native file starts
with version = 1 and unknown keys are always rejected. Legacy
Sesh-compatible files (no version key) still load during the migration
period and print a deprecation warning on stderr; see
Legacy migration.
Lookup order:
--config PATHHERDR_SESH_CONFIG(an error if the file does not exist)${HERDR_PLUGIN_CONFIG_DIR}/config.toml${HERDR_PLUGIN_CONFIG_DIR}/sesh.tomlas a legacy fallback~/.config/herdr-sesh/config.toml~/.config/herdr-sesh/sesh.tomlas a legacy fallback~/.config/sesh/sesh.tomlas a legacy fallback
Explicit paths (--config, HERDR_SESH_CONFIG) may hold either schema: a
top-level version key selects native decoding, otherwise the file is treated
as legacy. config path prints the file that would load, or the native
config.toml destination when none exists. config init writes a native
starter file only when no config exists anywhere in the lookup order; an
existing config (legacy included) is printed instead so init can never shadow
it. With HERDR_SESH_CONFIG set to a missing path, init creates the starter
at that exact path. config validate [PATH] strictly validates the active or
specified config and prints its resolved path on success. It returns an error
when no config exists; legacy files remain valid but emit the migration warning.
Create your configuration¶
Run these commands in a shell with Herdr available. jq is used to read
the installed plugin path from Herdr's JSON output.
Herdr creates HERDR_PLUGIN_CONFIG_DIR and HERDR_PLUGIN_STATE_DIR for the
plugin. Keep user configuration in the config directory and runtime state in
the state directory.
Add a workspace with a tab¶
Open the file printed by config path. Keep its version = 1 line, then add
the following entries, replacing the path with an existing Git checkout:
If the existing file uses the legacy schema, migrate it before adding native entries. Use unique workspace and tab names if the file already defines them.
[[tab]]
name = "git"
startup = "git status"
[[workspace]]
name = "my-project"
path = "~/projects/my-project"
tabs = ["git"]
Validate the file:
Open the picker, search for my-project, and press Enter. A newly created
workspace receives the named git tab and runs git status there. Selecting
an existing workspace focuses it; it does not recreate its tabs or rerun startup
commands.
Example¶
This is a customization example, not a dump of the defaults. Omitted settings use the defaults described in Settings.
version = 1 # (1)!
[list]
cache = true
source_order = ["herdr", "config", "zoxide", "dir"] # (2)!
blacklist = ["^scratch$"]
[naming]
path_components = 1
[picker]
show_icons = true
show_path = true
show_preview = true
preview_mode = "command"
prioritize_home = false
herdr_theme_inherit = true
replace_worktree_icon = true
prompt = "Sesh> "
placeholder = "Search workspaces"
separator_aware = true
workspace_sort = "agent"
show_last_workspace = true
show_last_workspace_path = false
[workspace_defaults]
startup = "git status"
preview = "eza --icons=always --color=always -la {}"
[[tab]]
name = "git"
startup = "git status"
[[workspace]]
name = "brain"
path = "~/brain"
disable_startup = true
tabs = ["git"] # (3)!
[[rule]]
path_glob = "~/projects/**"
startup = "git status"
preview = "eza --icons=always --color=always -la {}"
tabs = ["git"]
- Native configuration requires this schema version; unknown keys are rejected.
- Source order controls how results are combined; picker sorting affects Herdr rows.
- Tab names refer to
[[tab]]definitions. They are created when the workspace is new.
Legacy migration¶
Legacy Sesh-compatible files keep loading for at least one released version.
Run config migrate to convert the active legacy file automatically:
Conversion intentionally modernizes two defaults: when
tui.show_icons was never set, the native config enables icons; and the former
colorless default preview (eza --icons=always -la {}) is replaced by the
color-forced runtime default. Explicit icon settings and custom preview commands
are preserved. The command flattens any import files into a native
config.toml, leaves the legacy file untouched, and prints the new path.
Pass --config PATH to convert a specific file. The command refuses to
overwrite an existing native file unless --force is passed; even with
--force, unrelated or invalid config.toml files are never replaced. The
native file is installed atomically with 0600 permissions. A specific file can
also be supplied positionally, for example
herdr-sesh config migrate ~/.config/sesh/sesh.toml --force. Values the native
schema rejects (invalid regexes, duplicate names, missing tab references) fail
with an error before anything is written. Comments and key order do not survive
conversion.
Delete the legacy file once the native one looks right. If
HERDR_SESH_CONFIG selects the legacy file, point it at the printed native path
before deleting the legacy file. A legacy file already named config.toml
cannot be migrated in place, even with --force; rename it first so migration
can leave the source untouched.
Manual migration: legacy → native key reference
Rename keys as follows; unlisted fields keep their meaning under the renamed table.
| Legacy key | Native key |
|---|---|
cache |
list.cache |
strict_mode |
Removed; native decoding is always strict |
import |
Unsupported in native version 1 |
blacklist |
list.blacklist |
sort_order |
list.source_order |
dir_length |
naming.path_components |
separator_aware |
picker.separator_aware |
tui.show_icons |
picker.show_icons |
tui.herdr_theme_inherit |
picker.herdr_theme_inherit |
tui.replace_worktree_icon |
picker.replace_worktree_icon |
tui.show_last_workspace |
picker.show_last_workspace |
tui.show_last_workspace_path |
picker.show_last_workspace_path |
tui.prompt |
picker.prompt |
tui.placeholder |
picker.placeholder |
tui.default_sort |
picker.workspace_sort (workspace, recent, or agent) |
default_session.startup_command |
workspace_defaults.startup |
default_session.preview_command |
workspace_defaults.preview |
session[] |
workspace[] |
session[].startup_command |
workspace[].startup |
session[].preview_command |
workspace[].preview |
session[].disable_startup_command |
workspace[].disable_startup |
session[].windows |
workspace[].tabs |
window[] |
tab[] |
window[].startup_script |
tab[].startup |
window[].path |
tab[].path |
wildcard[] |
rule[] |
wildcard[].pattern |
rule[].path_glob |
wildcard[].startup_command |
rule[].startup |
wildcard[].preview_command |
rule[].preview |
wildcard[].disable_startup_command |
rule[].disable_startup |
wildcard[].windows |
rule[].tabs |
Stray version keys in legacy files
A legacy file containing a stray top-level version key was silently
ignored before and now selects strict native decoding, which fails hard on
the remaining legacy keys. Remove the key or migrate the file.
Legacy tmux_command, tmuxp, and tmuxinator fields have no Herdr
equivalent; native decoding rejects them like any other unknown key.
Settings¶
[list]¶
| Field | Runtime effect |
|---|---|
cache |
Caches normal deduplicated list results for five seconds in HERDR_PLUGIN_STATE_DIR, scoped to the resolved config file. It does not cache list --blacklisted, list --hide-duplicates=false, picker, or connect. |
source_order |
Orders sources among herdr, config, zoxide, and dir. Unknown or duplicated names are rejected; sources omitted from the list are appended. |
blacklist |
Treats each value as a regular expression matched against workspace names. Normal listings hide matches; list --blacklisted shows them. Invalid regexes are rejected. |
[naming]¶
| Field | Runtime effect |
|---|---|
path_components |
Sets the number of path components used by the directory-name fallback for a newly created direct-path workspace. Git repositories keep their repository-derived name. Must be at least 1 (the default). |
[keys]¶
cycle_preview_mode changes the native picker's preview-mode shortcut. Omit it
for "ctrl+o", choose a key such as "alt+p" or "f2", or set it to "" to
disable cycling and hide the shortcut hint. Key names use Bubble Tea's exact,
case-sensitive spelling: a single printable character or a named key, with
modifiers joined by + in ctrl, alt, shift, meta, hyper, super order.
Unsupported names, duplicate modifiers, and incorrect modifier order are rejected
as configuration errors. For shifted printable keys, configure the resulting
character ("P" rather than "shift+p", or "?" rather than "shift+/").
Shift-only printable spellings are rejected because key events report the text;
combinations such as "ctrl+shift+p" and "shift+f2" remain valid. When Shift is
combined with other modifiers, use the unshifted base key: "ctrl+shift+p", not
"ctrl+shift+P", and "alt+shift+/", not "alt+shift+?".
The configured shortcut
takes precedence over other native picker bindings, so choose an unused key.
Disabling cycling leaves the initial [picker].preview_mode in effect and
returns keys to their normal picker/input handling. This does not affect fzf or
configure shortcuts in Herdr itself.
[picker]¶
| Field | Runtime effect |
|---|---|
show_icons |
Shows Nerd Font source icons in the native picker. The default is false; source names remain visible when icons are hidden. |
show_path |
Shows the path column in the native picker when space permits (default true). Set it to false to hide the column and give the side-by-side preview 50% of the available width initially. The divider remains draggable; narrow terminals keep stacked previews. This does not affect path matching, the last-workspace footer, or fzf. |
show_preview |
Shows the preview panel in the native picker. The default is true; set it to false to give the workspace list the full available width and height without running preview commands. This does not change fzf preview behavior. |
preview_mode |
Sets the native picker's initial preview to command (the configured preview command or built-in fallback, the default) or pane (the active pane of the selected Herdr workspace, refreshed once per second). Press Ctrl+O (or keys.cycle_preview_mode) to switch modes while the picker is open. show_preview = false still disables previews. This does not affect fzf or herdr-sesh preview. |
prioritize_home |
Controls exact case-insensitive home searches in the native picker. The default is true, which promotes the actual home-directory session ahead of real-name and ordinary path matches. Set it to false to keep real-name matches first, then path matches in their existing order; the actual home-directory session remains searchable through the exact home alias. |
herdr_theme_inherit |
Inherits colors from Herdr's active theme. The default is true; set it to false to keep the native picker's built-in colors. |
replace_worktree_icon |
Replaces the Herdr sheep icon with ↳ for linked worktree rows. The default is true. Set it to false to keep the sheep icon (or plain [herdr] when icons are hidden); the purple type color and tree branches remain. |
prompt |
Replaces the picker prompt. An empty value uses Sesh>. |
placeholder |
Replaces the picker placeholder. An empty value uses Filter workspaces. |
separator_aware |
Makes native and fzf picker searches treat -, _, /, and . as spaces. |
workspace_sort |
Sets the native picker's initial Herdr workspace order to workspace (Herdr's order, the default), recent (most recently visited first), or agent (agent-status priority). Press Ctrl+R to cycle workspace → recent → agent while the picker is open. This setting does not affect fzf or JSON output. |
show_last_workspace |
Shows the workspace targeted by herdr-sesh last in the picker footer. The default is true; set it to false to hide the footer without disabling history tracking or the last command. |
show_last_workspace_path |
Shows the Herdr workspace working directory beside the last workspace name. The default is true; set it to false to show only the workspace name. |
Search ranking¶
The native picker matches names and paths case-insensitively and places name
matches before path-only matches, retaining the existing order within each
group. For example, searching api puts a workspace named api ahead of one
named web whose path contains /api/. workspace_sort determines the Herdr
workspace order within these match groups.
An exact home query also matches the actual home-directory session, even if
its name does not contain home. With prioritize_home = true (the default),
that session comes first. With false, it stays in the path-match group.
These ranking rules apply only to the native picker; fzf uses its own ranking.
Preview controls¶
When the native picker shows the preview beside the workspace list, click and drag the vertical divider with the left mouse button to resize it. Both panels keep a minimum width. The chosen width lasts until the picker closes; narrow terminals continue to show the preview below the list.
See Keybindings for switching between command and active-pane previews. Pane mode reads visible terminal contents without focusing the selected workspace or running its configured preview command.
Workspace history¶
herdr-sesh last, the previous-workspace footer, and recent sorting use history
that also tracks workspace switches made through Herdr's own controls or CLI.
The installed plugin starts tracking automatically through startup, focus, and
close hooks; no additional keybinding or configuration is required. Closed
workspaces are removed from history.
Herdr 0.9.0 sidebar navigation
Herdr 0.9.0 suppresses lifecycle focus events for client navigation, so
sidebar switches can leave history stale and make last select the wrong
workspace. Use a running Herdr server on 0.9.1 or newer for the upstream
fix; rebuilding herdr-sesh alone does not fix the missing events.
See issue #125.
History is separate for each Herdr session, using HERDR_SOCKET_PATH to select
${HERDR_PLUGIN_STATE_DIR}/history/<socket-hash>/history.json. Existing unscoped
history is copied on first use for the default session only; named sessions
start with their own history. Hiding the footer with show_last_workspace = false
does not disable history tracking or the last command.
See Workspace history tracking for lifecycle, persistence, and reconnect limitations.
Cursor and status indicators¶
Set HERDR_SESH_SMEAR_PRESET to choose the cursor animation:
| Preset | Effect |
|---|---|
crisp |
Fast cyan rail with a short violet line trail. This is the default. |
gooey |
Slower block cursor with a longer shaded trail and eased movement. |
ghost |
Soft diamond cursor with a dotted, low-contrast trail. |
Set HERDR_SESH_REDUCE_MOTION=1 (or true) to keep cursor movement
instantaneous without drawing any preset's trail.
Open Herdr workspaces show the agent state reported by Herdr: an animated amber
Jump spinner (⢄⢂⢁⡁⡈⡐⡠) while working, red ◉ when blocked, green ✓ when idle,
and teal ● when done. Workspaces with an unknown state have no indicator.
Herdr calls an actively running agent working.
The native picker's agent sort mode orders recognized states as blocked →
done → working → idle, followed by workspaces with no agent or an unknown
state. Ties retain Herdr's original workspace order, including unrecognized
future states. Live status refreshes update this order without changing the
selected workspace. Sorting only rearranges Herdr rows within their configured
source-order slots; it does not move them ahead of config, zoxide, or dir
rows when list.source_order puts those sources first.
Picker colors¶
By default, the native picker inherits colors from Herdr's own theme so it matches the running Herdr UI. Disable inheritance to keep the picker's built-in colors:
When enabled, it reads the same config file Herdr uses (HERDR_CONFIG_PATH,
then $XDG_CONFIG_HOME/herdr/config.toml, then
~/.config/herdr/config.toml) and resolves [theme] name against Herdr's
built-in themes:
catppuccin (default), catppuccin-latte, tokyo-night, tokyo-night-day,
dracula, nord, gruvbox, gruvbox-light, one-dark, one-light,
solarized, solarized-light, kanagawa, kanagawa-lotus, rose-pine,
rose-pine-dawn, and vesper. Common aliases (catppuccin-mocha,
tokyonight, onedark, …) are accepted, as are [theme.custom] overrides on
top of any base theme.
| Herdr token | Picker role |
|---|---|
accent |
Prompt, cursor, and selection rail |
mauve |
Title, section headers, search matches, smear trail |
text |
Row labels |
subtext0 |
Paths, counts, help text |
green |
Idle agents (✓) |
yellow |
Working agents (spinner) and the empty-state message |
red |
Blocked agents (◉) |
overlay1 |
Ghost cursor trail |
Custom tokens that are unknown or not a #RGB/#RRGGBB hex value leave that
role's built-in color in place, so partial [theme.custom] tables only affect
the roles they define. The ANSI-based terminal theme has no fixed palette to
inherit; the picker keeps its built-in colors there unless you add explicit
overrides.
The native picker marks a linked Git worktree workspace with a purple
↳ herdr type label, replacing the normal Herdr sheep icon, and groups it
immediately beneath its open parent workspace in workspace, recent, and agent
sort modes, matching Herdr's sidebar. In agent mode, the highest-priority status
on any family member ranks the whole family; the parent remains first and its
children follow in agent-priority order. With icons disabled, the label is [↳ herdr].
When the parent is visible, ├─ and └─ branches reinforce the family in the
workspace-name column. Wide layouts show the worktree path in the secondary
column when space permits; narrow layouts retain the purple type label. This is
automatic and does not depend on show_icons. Set
picker.replace_worktree_icon = false to retain the normal sheep icon or plain
[herdr] label while keeping the other child-worktree cues. If Herdr reports a
linked worktree but no single open parent can be resolved, the row remains
ungrouped rather than inventing a parent.
[workspace_defaults]¶
| Field | Runtime effect |
|---|---|
startup |
Fallback command run after a new Herdr workspace is created. {} is replaced with the workspace path. |
preview |
Fallback command used by preview and the native picker. {} is replaced with the workspace path. Absent or empty values use the built-in eza preview. |
[[workspace]]¶
| Field | Runtime effect |
|---|---|
name |
Workspace label and connect target. Must be non-empty and unique. |
path |
Workspace path; ~/ is expanded before it is sent to Herdr. Must be non-empty. |
startup |
Workspace-specific startup command. |
preview |
Workspace-specific preview command. |
disable_startup |
Suppresses startup execution when true. |
tabs |
Names of [[tab]] entries to create as Herdr tabs. Every referenced tab must exist. |
Startup commands are selected in this order: the explicit workspace command,
the first matching rule command, then workspace_defaults.startup. Preview
commands use the same explicit workspace, rule, then default order.
[[tab]]¶
| Field | Runtime effect |
|---|---|
name |
Name referenced by a workspace or rule tabs list and used as the Herdr tab label. Must be non-empty and unique. |
path |
Optional tab working directory. Without it, the workspace path is used; ~/ is expanded. |
startup |
Command run in the new tab. {} is replaced with that tab's working directory. |
[[rule]]¶
Rule startup, preview, and disable settings apply to every matching workspace when the corresponding explicit workspace field is unset. Rule tabs apply only to discovered or direct-path workspaces. The first matching rule wins.
| Field | Runtime effect |
|---|---|
path_glob |
Path glob. *, ?, and character classes use filepath.Match semantics; a trailing /** matches the base directory and all descendants. Must be non-empty and compile. |
startup |
Startup command for a matching path. |
preview |
Preview command for a matching path. |
disable_startup |
Suppresses rule and default startup behavior for a matching path when true. |
tabs |
[[tab]] entries created for a matching discovered or direct-path workspace, not a configured workspace. |