๐Ÿ‘ An opinionated HerdR launcher for how I currently do multi-agent dev in Git repos A developer created a Bash script called 'herd' that launches a HerdR workspace for the current Git repository, enabling multi-agent development with separate workspaces per worktree and parallel agentic loops. The script automates workspace creation and tab setup for agent, editor, changes, diff, run, and terminal views. | /usr/bin/env bash | | herd - summon a HerdR workspace for the current git repo. | | | | Model: | | ๐Ÿ‘ 1 worktree = 1 HerdR workspace | | ๐Ÿค– 1 agentic loop per workspace | | ๐Ÿงผ no repo-local config | | ๐ŸŒณ parallelism across worktrees | | | | Usage: | | cd /path/to/repo | | herd | | | | set -euo pipefail | | | | HERDR BIN="${HERDR BIN:-herdr}" | | | | TABS= | | "๐Ÿค– agent" | | "๐Ÿ“ editor" | | "๐ŸŒฟ changes" | | "๐Ÿ”€ diff" | | "๐Ÿƒ run" | | "๐Ÿ–ฅ๏ธ term" | | | | | | ---------- colors ---------- | | | | if -t 1 ; then | | BOLD="$ printf '\033 1m' " | | DIM="$ printf '\033 2m' " | | RED="$ printf '\033 31m' " | | GREEN="$ printf '\033 32m' " | | YELLOW="$ printf '\033 33m' " | | BLUE="$ printf '\033 34m' " | | MAGENTA="$ printf '\033 35m' " | | CYAN="$ printf '\033 36m' " | | RESET="$ printf '\033 0m' " | | else | | BOLD="" | | DIM="" | | RED="" | | GREEN="" | | YELLOW="" | | BLUE="" | | MAGENTA="" | | CYAN="" | | RESET="" | | fi | | | | say { | | printf '%b\n' "$ " | | } | | | | die { | | say "${RED}๐Ÿ’ฅ error:${RESET} $ " &2 | | exit 1 | | } | | | | ok { | | say "${GREEN}โœ“${RESET} $ " | | } | | | | info { | | say "${CYAN}โžœ${RESET} $ " | | } | | | | warn { | | say "${YELLOW}โš ${RESET} $ " | | } | | | | ---------- basics ---------- | | | | need cmd { | | command -v "$1" /dev/null 2 &1 || die "missing command: $1" | | } | | | | repo root { | | git rev-parse --show-toplevel 2 /dev/null | | } | | | | workspace name for repo { | | local root="$1" | | local repo parent | | | | repo="$ basename "$root" " | | parent="$ basename "$ dirname "$root" " " | | | | Human-readable and stable: | | carrefour/alertnet-back | | axone-protocol/prolog | | echo "$parent/$repo" | | } | | | | workspace id by label { | | local label="$1" | | local raw id | | | | raw="$ $HERDR BIN workspace list 2 /dev/null || true " | | -n "$raw" || return 1 | | | | id="$ printf '%s\n' "$raw" | jq -er --arg label "$label" ' | | def items: | | if type == "array" then . | | elif type == "object" then .result.workspaces // .workspaces // .result // | | else end; | | | | items | | | select type == "object" | | | select .label // .name // .title == $label | | | .workspace id // .id | | | select . = null and . = "" | | ' 2 /dev/null | head -n1 || true " | | | | if -n "$id" ; then | | printf '%s\n' "$id" | | return 0 | | fi | | | | Fallback for human/table output. | | id="$ printf '%s\n' "$raw" | | | awk -v want="$label" ' | | index $0, want { | | for i = 1; i <= NF; i++ { | | if $i ~ /^w :alnum: +$/ || $i ~ /^ 0-9 + : 0-9 + ? - 0-9 + ?$/ { | | print $i | | found = 1 | | exit 0 | | } | | } | | } | | END { if found exit 1 } | | ' || true " | | | | if -n "$id" ; then | | printf '%s\n' "$id" | | return 0 | | fi | | | | return 1 | | } | | | | tab id by label { | | local workspace id="$1" | | local label="$2" | | local raw id | | | | raw="$ $HERDR BIN tab list --workspace "$workspace id" 2 /dev/null || true " | | -n "$raw" || return 1 | | | | id="$ printf '%s\n' "$raw" | jq -er --arg label "$label" ' | | def items: | | if type == "array" then . | | elif type == "object" then .result.tabs // .tabs // .result // | | else end; | | | | items | | | select type == "object" | | | select .label // .name // .title == $label | | | .tab id // .id | | | select . = null and . = "" | | ' 2 /dev/null | head -n1 || true " | | | | if -n "$id" ; then | | printf '%s\n' "$id" | | return 0 | | fi | | | | Fallback for human/table output. | | id="$ printf '%s\n' "$raw" | | | awk -v want="$label" ' | | index $0, want { | | for i = 1; i <= NF; i++ { | | if $i ~ /^w :alnum: +:t :alnum: +$/ || $i ~ /^ 0-9 +: 0-9 +$/ || $i ~ /^ 0-9 +$/ { | | print $i | | found = 1 | | exit 0 | | } | | } | | } | | END { if found exit 1 } | | ' || true " | | | | if -n "$id" ; then | | printf '%s\n' "$id" | | return 0 | | fi | | | | return 1 | | } | | initial tab id { | | local workspace id="$1" | | local raw id | | | | raw="$ $HERDR BIN tab list --workspace "$workspace id" 2 /dev/null || true " | | -n "$raw" || return 1 | | | | id="$ printf '%s\n' "$raw" | jq -er ' | | def items: | | if type == "array" then . | | elif type == "object" then .result.tabs // .tabs // .result // | | else end; | | | | items | | | select type == "object" | | | select .label // .name // .title == "1" or .label // .name // .title == "" | | | .tab id // .id | | | select . = null and . = "" | | ' 2 /dev/null | head -n1 || true " | | | | if -n "$id" ; then | | printf '%s\n' "$id" | | return 0 | | fi | | | | return 1 | | } | | | | pane id by tab id { | | local workspace id="$1" | | local tab id="$2" | | local raw id | | | | raw="$ $HERDR BIN pane list --workspace "$workspace id" --tab "$tab id" 2 /dev/null || true " | | -n "$raw" || return 1 | | | | id="$ printf '%s\n' "$raw" | jq -er ' | | def items: | | if type == "array" then . | | elif type == "object" then .result.panes // .panes // .result // | | else end; | | | | items | | | select type == "object" | | | .pane id // .id | | | select . = null and . = "" | | ' 2 /dev/null | head -n1 || true " | | | | if -n "$id" ; then | | printf '%s\n' "$id" | | return 0 | | fi | | | | Fallback for human/table output. | | id="$ printf '%s\n' "$raw" | | | awk ' | | { | | for i = 1; i <= NF; i++ { | | if $i ~ /^w :alnum: +:t :alnum: +:p :alnum: +$/ || $i ~ /^ 0-9 +- 0-9 +$/ || $i ~ /^ 0-9 +$/ { | | print $i | | found = 1 | | exit 0 | | } | | } | | } | | END { if found exit 1 } | | ' || true " | | | | if -n "$id" ; then | | printf '%s\n' "$id" | | return 0 | | fi | | | | return 1 | | } | | | | pane id from create tab response { | | local raw="$1" | | local id | | -n "$raw" || return 1 | | | | id="$ printf '%s\n' "$raw" | jq -er ' | | def pane id from $x : | | if $x | type == "object" then $x.pane id // $x.id // empty | | else empty end; | | | | . as $root | | | .result // {} as $result | | | | | pane id from $result.root pane , | | pane id from $result.pane , | | pane id from $result.active pane , | | pane id from $result.created pane , | | pane id from $root.root pane , | | pane id from $root.pane , | | $result.panes // $result.tab.panes // $root.panes // $root.tab.panes // | . ? | pane id from . | | | | | select . = null and . = "" | | ' 2 /dev/null | head -n1 || true " | | | | if -n "$id" ; then | | printf '%s\n' "$id" | | return 0 | | fi | | | | return 1 | | } | | | | ---------- herdr ops ---------- | | | | create workspace { | | local label="$1" | | local cwd="$2" | | | | "$HERDR BIN" workspace create --cwd "$cwd" --label "$label" | | } | | | | focus workspace { | | local workspace id="$1" | | "$HERDR BIN" workspace focus "$workspace id" | | } | | | | create tab { | | local workspace id="$1" | | local label="$2" | | | | "$HERDR BIN" tab create --workspace "$workspace id" --label "$label" --no-focus | | } | | rename tab { | | local tab id="$1" | | local label="$2" | | | | "$HERDR BIN" tab rename "$tab id" "$label" | | } | | | | close tab { | | local tab id="$1" | | | | "$HERDR BIN" tab close "$tab id" | | } | | | | run in pane { | | local pane id="$1" | | local command="$2" | | | | "$HERDR BIN" pane run "$pane id" "$command" | | } | | | | split pane horizontal { | | local pane id="$1" | | local output="" | | | | HerdR direction names are right/down. "down" creates a visual horizontal split. | | if output="$ $HERDR BIN pane split "$pane id" --direction down --no-focus 2 &1 "; then | | return 0 | | fi | | | | if output="$ $HERDR BIN pane split --pane "$pane id" --direction down --no-focus 2 &1 "; then | | return 0 | | fi | | | | if output="$ $HERDR BIN pane split "$pane id" down 2 &1 "; then | | return 0 | | fi | | | | warn "could not split runner pane horizontally: $output" | | return 1 | | } | | | | shell command { | | local root="$1" | | local label="$2" | | | | printf "cd '%s' && clear && printf '๐Ÿ‘ %%s\\n๐Ÿ“ %%s\\n\\n' '%s' '%s' && exec %s" \ | | "$root" "$label" "$root" "${SHELL:-/bin/bash}" | | } | | | | omp command { | | local root="$1" | | local label="$2" | | | | if command -v omp /dev/null 2 &1; then | | printf "cd '%s' && omp" "$root" | | else | | printf "cd '%s' && clear && printf '๐Ÿง  %%s\\n๐Ÿ“ %%s\\n\\nโš  omp not found in PATH\\n\\n' '%s' '%s' && exec %s" \ | | "$root" "$label" "$root" "${SHELL:-/bin/bash}" | | fi | | } | | | | editor command { | | local root="$1" | | | | if command -v nvim /dev/null 2 &1; then | | printf "cd '%s' && nvim ." "$root" | | elif command -v vim /dev/null 2 &1; then | | printf "cd '%s' && vim ." "$root" | | else | | printf "cd '%s' && clear && printf '๐Ÿ“ editor\\n๐Ÿ“ %%s\\n\\nโš  nvim not found in PATH\\n\\n' '%s' && exec %s" \ | | "$root" "$root" "${SHELL:-/bin/bash}" | | fi | | } | | | | git command { | | local root="$1" | | | | if command -v lazygit /dev/null 2 &1; then | | printf "cd '%s' && lazygit" "$root" | | else | | printf "cd '%s' && git status && exec %s" "$root" "${SHELL:-/bin/bash}" | | fi | | } | | | | livediff command { | | local root="$1" | | local label="$2" | | | | if command -v livediff /dev/null 2 &1; then | | printf "cd '%s' && livediff" "$root" | | else | | printf "cd '%s' && clear && printf '๐Ÿ”€ %%s\\n๐Ÿ“ %%s\\n\\nโš  livediff not found in PATH\\n\\n' '%s' '%s' && exec %s" \ | | "$root" "$label" "$root" "${SHELL:-/bin/bash}" | | fi | | } | | | | ---------- main ---------- | | | | main { | | need cmd git | | need cmd jq | | need cmd "$HERDR BIN" | | | | local root workspace workspace id created="false" | | | | root="$ repo root " || die "not inside a git repository" | | workspace="$ workspace name for repo "$root" " | | | | say | | say "${BOLD}${MAGENTA}๐Ÿ‘ herd${RESET} ${DIM}summoning repo workspace${RESET}" | | say "${DIM}โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€${RESET}" | | say "${BLUE}repo:${RESET} $root" | | say "${BLUE}workspace:${RESET} $workspace" | | say | | | | if workspace id="$ workspace id by label "$workspace" 2 /dev/null "; then | | ok "workspace exists ${DIM} $workspace id${RESET}" | | else | | info "creating workspace" | | create workspace "$workspace" "$root" /dev/null | | created="true" | | | | workspace id="$ workspace id by label "$workspace" 2 /dev/null || true " | | if -z "$workspace id" ; then | | warn "workspace created, but could not resolve its id from HerdR" | | warn "using workspace label as fallback: $workspace" | | workspace id="$workspace" | | fi | | | | ok "workspace created ${DIM} $workspace id${RESET}" | | fi | | | | say | | | | local tab tab id pane id command response initial tab | | initial tab="$ initial tab id "$workspace id" 2 /dev/null || true " | | | | for tab in "${TABS @ }"; do | | info "creating tab: ${BOLD}$tab${RESET}" | | response="$ create tab "$workspace id" "$tab" " | | pane id="$ pane id from create tab response "$response" 2 /dev/null || true " | | | | tab id="$ tab id by label "$workspace id" "$tab" 2 /dev/null || true " | | if -z "$tab id" ; then | | warn "tab created, but could not resolve its id: $tab" | | fi | | | | if -z "$pane id" && -n "$tab id" ; then | | pane id="$ pane id by tab id "$workspace id" "$tab id" 2 /dev/null || true " | | fi | | | | if -z "$pane id" ; then | | warn "tab created, but could not resolve root pane: $tab" | | continue | | fi | | | | command="" | | case "$tab" in | | "๐Ÿค– agent" | | command="$ omp command "$root" "$tab" " | | ;; | | "๐Ÿ“ editor" | | command="$ editor command "$root" " | | ;; | | "๐ŸŒฟ changes" | | command="$ git command "$root" " | | ;; | | "๐Ÿ”€ diff" | | command="$ livediff command "$root" "$tab" " | | ;; | | "๐Ÿƒ run" | | split pane horizontal "$pane id" || true | | ;; | | | | Default HerdR shell is enough for generic terminals. | | command="" | | ;; | | esac | | | | if -n "$command" ; then | | run in pane "$pane id" "$command" | | fi | | ok "tab ready: ${BOLD}$tab${RESET}" | | | | done | | | | if -n "$initial tab" ; then | | if close tab "$initial tab" /dev/null 2 &1; then | | ok "initial tab removed" | | else | | warn "could not remove initial tab: $initial tab" | | fi | | fi | | | | say | | info "focusing workspace" | | focus workspace "$workspace id" | | | | say | | if "$created" = "true" ; then | | say "${GREEN}โœจ herd assembled.${RESET}" | | else | | say "${GREEN}โœจ herd restored.${RESET}" | | fi | | say | | } | | | | main "$@" |