Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions bin/omarchy-refresh-herdr
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/bin/bash

# omarchy:summary=Overwrite the user herdr config with the Omarchy default and reload herdr.

omarchy-refresh-config herdr/config.toml
omarchy-restart-herdr
5 changes: 5 additions & 0 deletions bin/omarchy-restart-herdr
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/bin/bash

# omarchy:summary=Reload herdr if running with the latest configuration

herdr server reload-config >/dev/null 2>&1 || true
94 changes: 94 additions & 0 deletions config/herdr/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# Mirrors the Omarchy tmux config in config/tmux/tmux.conf
# tmux session -> herdr workspace, tmux window -> herdr tab, tmux pane -> herdr pane

[theme]
# tmux ran on the terminal's own palette (bg=default, fg=default, ANSI blue accents)
name = "terminal"

[theme.custom]
# The active tab is drawn as panel_bg text on an accent background, so panel_bg
# has to be dark for it to read - same colors as status-left's "#[fg=black,bg=blue]"
panel_bg = "black"

[terminal]
# Matches -c "#{pane_current_path}" on every split, window, and session
new_cwd = "follow"

[keys]
prefix = "ctrl+space"

# Config and help
reload_config = "prefix+q"
help = "prefix+?"
detach = "prefix+d"

# Copy mode
copy_mode = "prefix+["

# Panes
split_horizontal = ["prefix+h", "alt+enter"]
split_vertical = ["prefix+v", "alt+shift+enter"]
close_pane = ["prefix+x", "alt+esc"]
zoom = "prefix+z"
last_pane = "prefix+;"

focus_pane_left = "ctrl+alt+left"
focus_pane_down = "ctrl+alt+down"
focus_pane_up = "ctrl+alt+up"
focus_pane_right = "ctrl+alt+right"

resize_mode = ["prefix+ctrl+left", "prefix+ctrl+down", "prefix+ctrl+up", "prefix+ctrl+right"]

# No tmux equivalent; herdr's default prefix+shift+p is taken by previous session
rename_pane = "prefix+shift+o"

# Windows -> tabs
new_tab = "prefix+c"
rename_tab = "prefix+r"
close_tab = "prefix+k"
switch_tab = ["prefix+1..9", "alt+1..9"]
previous_tab = ["prefix+p", "alt+left"]
next_tab = ["prefix+n", "alt+right"]

# Sessions -> workspaces
new_workspace = "prefix+shift+c"
rename_workspace = "prefix+shift+r"
close_workspace = "prefix+shift+k"
previous_workspace = ["prefix+shift+p", "alt+up"]
next_workspace = ["prefix+shift+n", "alt+down"]

# Resize by ~5 columns/rows, like resize-pane -L 5 on C-M-S-arrows
[[keys.command]]
key = "ctrl+alt+shift+left"
type = "shell"
command = "herdr pane resize --current --direction left --amount 0.03"

[[keys.command]]
key = "ctrl+alt+shift+down"
type = "shell"
command = "herdr pane resize --current --direction down --amount 0.03"

[[keys.command]]
key = "ctrl+alt+shift+up"
type = "shell"
command = "herdr pane resize --current --direction up --amount 0.03"

[[keys.command]]
key = "ctrl+alt+shift+right"
type = "shell"
command = "herdr pane resize --current --direction right --amount 0.03"

[ui]
accent = "blue"

# tmux drew single-line dividers between adjacent panes
pane_gaps = false

# kill-window and kill-session never asked
confirm_close = false

# automatic-rename gave windows a name without prompting
prompt_new_tab_name = false

# set -g mouse on
mouse_capture = true
1 change: 1 addition & 0 deletions default/bash/aliases
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ alias cy='codex -s danger-full-access -a never'
alias d='docker'
alias r='rails'
alias t='tmux attach || tmux new -s Work'
alias h='herdr'
alias ic='tdl c'
alias ix='tdl cx'
alias icx='tdl c cx'
Expand Down
142 changes: 142 additions & 0 deletions default/bash/fns/herdr
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
# Echo a split ratio as a float
# Usage: hratio <numerator> <denominator>
hratio() {
awk -v a="$1" -v b="$2" 'BEGIN { printf "%.4f", a / b }'
}

# Split a herdr pane and echo the id of the new pane
# Usage: hsplit <pane_id> <right|down> <ratio> <cwd>
hsplit() {
herdr pane split "$1" --direction "$2" --ratio "$3" --cwd "$4" --no-focus |
jq -r '.result.pane.pane_id'
}

# Create a Herdr Dev Layout with editor, ai, and terminal
# Usage: hdl <c|cx|codex|other_ai> [<second_ai>]
hdl() {
[[ -z $1 ]] && { echo "Usage: hdl <c|cx|codex|other_ai> [<second_ai>]"; return 1; }
[[ -z $HERDR_PANE_ID ]] && { echo "You must start herdr to use hdl."; return 1; }

local current_dir="${PWD}"
local editor_pane ai_pane ai2_pane
local ai="$1"
local ai2="$2"

# Use HERDR_PANE_ID for the pane we're running in (stable even if focus moves)
editor_pane="$HERDR_PANE_ID"

# Name the current tab after the base directory name
herdr tab rename "$HERDR_TAB_ID" "$(basename "$current_dir")" >/dev/null

# Split tab vertically - top 85%, bottom 15%
hsplit "$editor_pane" down 0.85 "$current_dir" >/dev/null

# Split editor pane horizontally - AI on right 30%
ai_pane=$(hsplit "$editor_pane" right 0.7 "$current_dir")

# If second AI provided, split the AI pane vertically
if [[ -n $ai2 ]]; then
ai2_pane=$(hsplit "$ai_pane" down 0.5 "$current_dir")
herdr pane run "$ai2_pane" "$ai2" >/dev/null
fi

# Run ai in the right pane
herdr pane run "$ai_pane" "$ai" >/dev/null

# Run nvim in the left pane
herdr pane run "$editor_pane" "$EDITOR ." >/dev/null
}

# Create a Herdr Dev Square layout with editor, diff watch, terminal, and opencode
# Usage: hds
hds() {
[[ -n $1 ]] && { echo "Usage: hds"; return 1; }
[[ -z $HERDR_PANE_ID ]] && { echo "You must start herdr to use hds."; return 1; }

local current_dir="${PWD}"
local editor_pane diff_pane terminal_pane opencode_pane

editor_pane="$HERDR_PANE_ID"

herdr tab rename "$HERDR_TAB_ID" "$(basename "$current_dir")" >/dev/null

terminal_pane=$(hsplit "$editor_pane" down 0.5 "$current_dir")
diff_pane=$(hsplit "$editor_pane" right 0.5 "$current_dir")
opencode_pane=$(hsplit "$terminal_pane" right 0.5 "$current_dir")

herdr pane run "$editor_pane" "nvim ." >/dev/null
herdr pane run "$diff_pane" "hunk diff --watch" >/dev/null
herdr pane run "$opencode_pane" "opencode" >/dev/null
}

# Create multiple hdl tabs with one per subdirectory in the current directory
# Usage: hdlm <c|cx|codex|other_ai> [<second_ai>]
hdlm() {
[[ -z $1 ]] && { echo "Usage: hdlm <c|cx|codex|other_ai> [<second_ai>]"; return 1; }
[[ -z $HERDR_PANE_ID ]] && { echo "You must start herdr to use hdlm."; return 1; }

local ai="$1"
local ai2="$2"
local base_dir="$PWD"
local first=true

# Rename the workspace to the current directory name
herdr workspace rename "$HERDR_WORKSPACE_ID" "$(basename "$base_dir")" >/dev/null

for dir in "$base_dir"/*/; do
[[ -d $dir ]] || continue
local dirpath="${dir%/}"

if $first; then
# Reuse the current tab for the first project
herdr pane run "$HERDR_PANE_ID" "cd '$dirpath' && hdl $ai $ai2" >/dev/null
first=false
else
local pane_id
pane_id=$(herdr tab create --cwd "$dirpath" --no-focus | jq -r '.result.root_pane.pane_id')
herdr pane run "$pane_id" "hdl $ai $ai2" >/dev/null
fi
done
}

# Create a multi-pane swarm layout with the same command started in each pane (great for AI)
# Usage: hsl <pane_count> <command>
hsl() {
[[ -z $1 || -z $2 ]] && { echo "Usage: hsl <pane_count> <command>"; return 1; }
[[ -z $HERDR_PANE_ID ]] && { echo "You must start herdr to use hsl."; return 1; }

local count="$1"
local cmd="$2"
local current_dir="${PWD}"
local -a columns panes

herdr tab rename "$HERDR_TAB_ID" "$(basename "$current_dir")" >/dev/null

# Tile into a grid: ceil(sqrt(count)) columns, rows spread across them
local cols=1
while (( cols * cols < count )); do ((cols++)); done

# Even columns come from repeatedly splitting the first one at (n-k)/(n-k+1)
columns=("$HERDR_PANE_ID")
local k
for (( k = 1; k < cols; k++ )); do
columns+=("$(hsplit "${columns[0]}" right "$(hratio $((cols - k)) $((cols - k + 1)))" "$current_dir")")
done

# Split each column into its share of rows, again evenly
local col index rows j
for (( index = 0; index < cols; index++ )); do
col="${columns[index]}"
rows=$(( count / cols ))
(( index < count % cols )) && (( rows++ ))
panes+=("$col")
for (( j = 1; j < rows; j++ )); do
panes+=("$(hsplit "$col" down "$(hratio $((rows - j)) $((rows - j + 1)))" "$current_dir")")
done
done

local pane
for pane in "${panes[@]}"; do
herdr pane run "$pane" "$cmd" >/dev/null
done
}
1 change: 1 addition & 0 deletions install/user/mise.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ omarchy-mise-install gemini
omarchy-mise-install gh
omarchy-mise-install copilot
omarchy-mise-install opencode
omarchy-mise-install herdr
omarchy-mise-install npm:playwright playwright
omarchy-mise-install pi
omarchy-mise-install npm:@kitlangton/ghui ghui
Expand Down
7 changes: 7 additions & 0 deletions migrations/1785191576.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
echo "Install herdr and seed its Omarchy config"

omarchy-mise-install herdr

# Only seed. A user who already has a herdr config keeps it; omarchy-refresh-herdr
# is the explicit way to take the shipped defaults.
[[ -f "$HOME/.config/herdr/config.toml" ]] || omarchy-refresh-config herdr/config.toml