Global config

View as markdown

Two files under ~/.shelbi/ hold hub-wide settings that apply across every project on your machine, independent of any single project's config mode: config.yaml (UI preferences) and keys.yaml (keybindings). Both are optional: absent or partial files fall back to built-in defaults, never an error.

Home resolution

~/.shelbi/ is the default base. It can be relocated via --root, then $SHELBI_ROOT, then $SHELBI_HOME, then a default baked in at install time (if any), before falling back to $HOME/.shelbi. Every path below joins a filename onto that base.

config.yaml

Per-user UI preferences. Distinct from shelbi.yaml (the per-project last_launched index) so a future shelbi config reset can wipe UI tweaks without touching hub bookkeeping. A file that omits a key or a block resolves to that setting's default.

A new install starts from a self-documenting config.yaml that sets theme: dark, desktop.contrast: 50, and a translucent desktop window (desktop.window_opacity: 85 and desktop.window_blur: 30). Whichever writes the file first (shelbi init, or a first settings change in either client) writes those values, and with no file at all both clients show them. An existing config.yaml is never rewritten: without these keys it keeps the defaults in the tables below (auto, contrast 0, an opaque window).

Path: ~/.shelbi/config.yaml

FieldTypeRequiredDefaultDescription
keymap.zen_togglechordnoalt-zLegacy. Set the Zen Mode chord in keys.yaml (defaults.global.zen_toggle) instead: a non-default value here is moved into keys.yaml on the next start and reset here, with a one-time notice.
themeauto | dark | lightnoautoColor theme for the TUI and the desktop app. See Theme.
desktopDesktopno—Desktop app settings. Ignored by the CLI and the TUI (except app_icon, which the TUI's About draws and its Settings ▸ General picks).
editorcommand | { app: <path> }no$EDITOR, else vimEditor Shelbi opens for "Edit in <editor>" (in the review interface and the workspace panel) and for the desktop app's external edits (an issue body, a workflow's Edit YAML). Hub-wide, so it follows you across projects. A command (hx, code --wait) runs in a pane; an app opens in the app. See Editor and diff tool.
diff_toolbuiltin | git | command | { app: <path> }nobuiltinWhat View Diff shows over a branch's changes: Shelbi's own diff view, git's difftool, a command in a pane, or an app. A project's review.diff_command takes precedence. See Editor and diff tool.
# ~/.shelbi/config.yaml
editor: code --wait
theme: light
desktop:
  contrast: 50
  window_opacity: 85
  window_blur: 30

Editor and diff tool

editor and diff_tool each take one of two forms. The rule is the same for both: a command-line tool opens as a pane (a terminal in the worktree, beside the agent), and an app opens in the app.

# ~/.shelbi/config.yaml
 
# A command: a program on your login PATH, with flags if you like.
editor: hx
diff_tool: difft
 
# An app: the path to its .app bundle.
editor: { app: /Applications/Zed.app }
diff_tool:
  app: /Applications/Kaleidoscope.app

A plain string is a command, so a config.yaml written before apps were supported keeps working unchanged.

Editor. A command runs in a pane in the worktree (exec <command>). An app opens the worktree with open -a <app> <worktree>, and "Edit in" leaves the current view selected rather than switching to a pane. Unset, Shelbi runs $EDITOR, then vim. A pane can't host an app, so anything that needs a terminal editor falls back past an app setting to $EDITOR, then vim.

Diff tool. View Diff compares the branch with its base (merge-base(<base branch>, HEAD)..HEAD). In precedence order it shows:

  1. the project's review.diff_command, when it sets one,
  2. diff_tool, when set to a tool,
  3. Shelbi's own diff view, which is what diff_tool: builtin asks for and what an unset setting means.

diff_tool: git runs git's own difftool (diff.guitool, else diff.tool). Any other diff_tool command runs in a pane through git difftool, one file at a time, with the two files appended (nvim -d, vimdiff). A command that names $LOCAL and $REMOTE itself is run as written. difft (difftastic) runs as git's external diff through less -R.

A diff_tool app opens the diff in the app through its own command-line helper, found on your login PATH or inside the app bundle:

AppHelperShows
FileMergeopendiffthe whole change (folders)
Kaleidoscopeksdiffthe whole change (folders)
Beyond Comparebcompthe whole change (folders)
Visual Studio Codecode --diffone file at a time
Sublime Mergesmergethe repository
P4Mergelaunchp4mergeone file at a time
Araxis Mergecompareone file at a time
DeltaWalkerDeltaWalkerone file at a time

Any other app gets the two folders with open -W -a <app>. If a listed app's helper isn't installed, View Diff says so on the status line.

Remote workspaces. Shelbi's own diff view reads a worktree on any machine, in both clients. A workspace on another machine also offers Edit, and an external diff tool, as panes that run on that machine. An app can't open a worktree on another machine. A review slot on another machine offers no Edit, and offers View Diff only for Shelbi's own view.

Labels. "Edit in <name>" uses the tool's name: Edit in Neovim for nvim, Edit in Helix for hx, Edit in Visual Studio Code for code --wait or the app, and the bundle's name for any other app. A command Shelbi doesn't know shows its program name with the first letter upper-cased.

Settings. Settings ▸ General has an Editor picker and a Diff tool picker in both clients. Each lists the default (no setting, so the fallback applies), the command-line tools and the apps Shelbi finds installed, Browse… (an app or a program on disk), and Other command… (a command line with its flags, saved when you press Enter). A pick saves at once, and the page shows what Shelbi runs and where it comes from (this key, $EDITOR, or the vim default). In the TUI, Browse asks for a path, with Tab completion.

Theme

theme picks the color theme both clients use. The two themes share one set of named colors, so the TUI and the desktop app look like the same product.

ValueTUIDesktop app
auto (default)dark (a terminal doesn't report whether it's light or dark)follows the system appearance, and switches live when the system switches
darkdarkdark
lightlight, for a terminal with a light backgroundlight

Both clients set it in Settings ▸ General. In the TUI a change saves at once and repaints every surface on the next frame, with no restart and no effect on running sessions. The TUI also reads config.yaml again whenever its Settings opens, so an edit made in a text editor or in the desktop app shows there. A missing key is auto (a new install's file sets dark). The dark theme follows your terminal's own palette for its status colors; the light theme pins every color, so it reads the same in any light terminal.

Desktop

The desktop: block holds the desktop app's settings. Its Settings ▸ General and Settings ▸ Terminal pages write these keys for you; every one is optional.

FieldTypeDefaultDescription
cli_pathpathdiscoveredThe shelbi CLI the app runs, overriding its discovery (the login-shell PATH, then the Homebrew prefixes and ~/.cargo/bin). A leading ~/ is expanded.
terminal_font_familystringGeist MonoFont family for the app's terminals.
terminal_font_sizeinteger (points)app defaultFont size for the app's terminals.
option_as_metafalse | true | left | rightfalseWhether Option acts as Meta in the terminals (Option+B sends ESC b) instead of typing the layout's Option characters. left / right make only that Option key Meta.
terminal_scrollbarauto | scrolling | alwaysautoWhen the terminals show a scrollbar: auto follows macOS's "Show scroll bars", scrolling only while scrolling, always whenever there is scrollback.
notifications.workspace_blockedbooltrueNotify when a workspace's agent stops for you (waiting for input, or a permission dialog).
notifications.review_readybooltrueNotify when a task reaches the review column.
notifications.orchestrator_waitingbooltrueNotify when the orchestrator waits for an answer or shows a permission dialog.
terminal_colors.<color>.regular / .bold#rrggbbtheme paletteTerminal ANSI colors you changed, per color (black, red, green, yellow, blue, magenta, cyan, white). Unset colors follow the app's palette.
window_opacitypercent100Window opacity. Values below the floor of 30 are clamped up; a fraction like 0.85 reads as 85. A new install's file sets 85.
window_blur0 to 500Blur radius, in points, for what shows through a translucent window. A new install's file sets 30.
contrastpercent0How far apart surfaces and their text sit; 100 is the strongest step. A new install's file sets 50.
app_iconcommand-grille | retro | racing-s | retro-racing-scommand-grilleThe icon in the Dock and in About: Command Grille, Retro, Racing S or Retro Racing S. Any case, and _ for -; an unknown value is ignored.

zen_toggle chords

The accepted values for the Zen toggle chord:

ValueKey
alt-zAlt+Z (default)
ctrl-backslashCtrl+\
ctrl-gCtrl+G
ctrl-shift-zCtrl+Shift+Z
nonedisabled — no chord toggles Zen Mode

keys.yaml

Overrides for Shelbi's keybindings, shared by the TUI and the desktop app. Both clients edit this file in Settings ▸ Keybindings (in the TUI press , in the sidebar, then 2; in the desktop app, ⌘2 while Settings is open). Built-in chords apply out of the box; this file layers on top of them, either globally (defaults) or per project (projects.<name>). Parse errors never fail the load. A mistyped entry is skipped and that action keeps its built-in chord.

Path: ~/.shelbi/keys.yaml

Shape

# ~/.shelbi/keys.yaml
defaults:                 # applies to every project
  <mode>:
    <action>: <chord | [chords] | null>
projects:                 # per-project overrides, keyed by project name
  <project-name>:
    <mode>:
      <action>: <chord | [chords] | null>

Bindings resolve in three layers, each overriding the last: built-in defaults → defaults.<mode>.<action> → projects.<name>.<mode>.<action>.

BlockTypeRequiredDefaultDescription
defaultsmap of mode → action → chord(s)no—Overrides applied to every project.
projectsmap of project name → (mode → action → chord(s))no—Overrides scoped to one project; win over defaults.

Leaf values

The value under each action is untyped so one bad entry only affects itself:

FormMeaning
scalar string (alt-z)Bind the action to a single chord.
list ([k, up])Bind the action to several chords.
[] (empty list)Deliberately unbind the action.
nullNo override — fall through to the layer below.

In the diff mode a chord can be two keys, separated by a space ('g g', 'z C', '] h'). Quote them: a bare ] isn't YAML. A key can't both do something by itself and start a two-key binding.

Modes

The valid top-level keys under defaults and each projects.<name>:

global, sidebar, kanban, popover, activity, diff, palette.

global bindings apply everywhere; the rest scope to their pane.

Actions

The action names accepted under each mode, with their built-in chords:

ModeActionBuilt-in
globalquitctrl-c
globalzen_togglealt-z
globalopen_palettectrl-p
globalfocus_sidebar / focus_mainctrl-h / ctrl-l
sidebarquitq,ctrl-c
sidebarnav_up / nav_downk,up / j,down
sidebaractivateenter,space
sidebarrefreshr
sidebaropen_settings',' (quoted: a bare comma isn't YAML)
kanbannav_left / nav_righth,left / l,right
kanbannav_up / nav_downk,up / j,down
kanbanmove_card_left / move_card_rightH / L
kanbanreorder_up / reorder_downK,shift-up / J,shift-down
kanbanopen_popoverenter,space
kanbancycle_workflow_filtertab
kanbanopen_workspaceo
popovercloseesc,enter,space,q
popoverscroll_up / scroll_downk,up / j,down
popoverpage_up / page_downpage-up,u / page-down,d
popovermove_left / move_rightH,shift-left / L,shift-right
popoveropen_workspaceo
popoveropen_prp
activityscroll_up / scroll_downk,up / j,down
activityscroll_endG,end
activityreset_filtera
activitytoggle_zen_filterz
activitytoggle_workspaces_filterw
diffcursor_down / cursor_upj,down / k,up
diffnext_file / prev_filel,ctrl-n,right / h,left
diffpage_down / page_upspace,ctrl-f,page-down / b,page-up
diffhalf_page_down / half_page_upctrl-d / ctrl-u
difftop / bottom'g g' / G
diffcenter_viewport / center_cursor'z z' / M
diffnext_change / prev_change'] h' / '[ h'
diffnext_comment / prev_comment'] c' / '[ c'
diffnext_empty_line / prev_empty_line'}' / '{'
difffind_forward / find_backwardf / F
difftill_forward / till_backwardt / T
diffrepeat_find';'
diffsearch / next_match / prev_match/ / n / N
difffile_palette / command_palette / helpctrl-p / ':' / '?'
difftoggle_split / toggle_blame / toggle_file_lists / B / ctrl-b,\
diffnext_hunk_filter / prev_hunk_filtertab / back-tab
difftoggle_hunk_fold / close_hunk_fold / open_hunk_fold'z a' / 'z c' / 'z o'
diffclose_file_fold / open_file_fold'z C' / 'z O'
diffclose_all_folds / open_all_folds'z M' / 'z R'
diffvisual_selectv,V
diffcomment / reply_or_refresh / toggle_threadenter / r / o
diffdelete_comment / clear_commentsd / D
diffyank / yank_all / send_to_agenty / Y / 'g Y'
diffsend_to_reviewer'g A'
diffrevert_hunk / revert_file'g r' / 'g R'
difftoggle_resolved / toggle_comment_targetx / C
diffsubmit_review / pr_infoR / i
diffopen_in_editorctrl-g
difftoggle_viewed / pick_commitm / c
palettecloseesc,ctrl-c,ctrl-p
paletteactivateenter
palettenav_up / nav_downup / down
palettebackspacebackspace

Most modes also carry refresh (r) and scroll-navigation actions (page_up/page_down, scroll_home). For the full list with the chords in effect after your keys.yaml, run shelbi config list-actions. shelbi config dump-keybindings prints the whole default keymap as a starting keys.yaml, and shelbi config check validates the file and reports errors and warnings.

A mode binding that reuses a global chord is dead in the sidebar, kanban, and activity modes: global bindings fire first. shelbi config check warns about it. The diff mode is the other way around: its bindings win while the diff view has focus, so ctrl-p opens its file palette there.

# rebind the palette opener globally, and add a second chord in one project
defaults:
  global:
    open_palette: ctrl-k
projects:
  myapp:
    kanban:
      move_card_left: [H, alt-h]

Sibling files

Two more files live under ~/.shelbi/ but are managed by Shelbi rather than hand-edited:

  • shelbi.yaml — hub-wide bookkeeping: a per-project last_launched index used by the project picker. Written when you launch a project.
  • events.log — the cross-project events log. Append-only state, not config.

See also