cd /news/developer-tools/an-opinionated-herdr-launcher-for-ho… Β· home β€Ί topics β€Ί developer-tools β€Ί article
[ARTICLE Β· art-70889] src=gist.github.com β†— pub= topic=developer-tools verified=true sentiment=Β· neutral

πŸ‘ 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.

read13 min views18 publishedJul 8, 2026

| #!/usr/bin/env bash | | | | | | | | | | | | | | | set -euo pipefail | | | | HERDR_BIN="${HERDR_BIN:-herdr}" | | | | TABS=( | | "πŸ€– agent" | | "πŸ“ editor" | | "🌿 changes" | | "πŸ”€ diff" | | "πŸƒ run" | | "πŸ–₯️ term" | | ) | | | | | | | 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} $*" | | } | | | | | | | 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")")" | | | | | | | 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 | | | | | id="$(printf '%s\n' "$raw" | | | awk -v want="$label" ' | | index($0, want) { | | for (i = 1; i <= NF; i++) { | | if ($i ~ /^w[[:alnum:]]+$/ || $i ~ /[1]+(:[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 | | | | | 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 ~ /[2]+:[0-9]+$/ || $i ~ /[3]+$/) { | | 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 | | | | | id="$(printf '%s\n' "$raw" | | | awk ' | | { | | for (i = 1; i <= NF; i++) { | | if ($i ~ /^w[[:alnum:]]+:t[[:alnum:]]+:p[[:alnum:]]+$/ || $i ~ /[4]+-[0-9]+$/ || $i ~ /[5]+$/) { | | 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 | | } | | | | | | | 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="" | | | | | 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() { | | 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 | | ;; | | *) | | | 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 "$@" |


  1. 0-9 β†©οΈŽ

  2. 0-9 β†©οΈŽ

  3. 0-9 β†©οΈŽ

  4. 0-9 β†©οΈŽ

  5. 0-9 β†©οΈŽ

── more in #developer-tools 4 stories Β· sorted by recency
── more on @herdr 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain β€” perfect for shipping the agent you just read about.

$git push zahid main
β†’ Live at https://your-agent.zahid.host βœ“
Get free account β†’ Pricing
from €0/mo Β· no card required
LIVE [news/an-opinionated-herdr…] indexed:0 read:13min 2026-07-08 Β· β€”