Przejdź do głównej zawartości

herdr: multiplekser agentów ze skryptowalnym API

Masz uruchomionych pięciu agentów. Jeden jest w środku refaktoru, jeden skończył jedenaście minut temu, jeden siedzi na promcie o uprawnienia od czasu, gdy poszedłeś po kawę, a dwóch robi coś, czego rekonstrukcja wymagałaby przeczytania 200 linii scrollbacka. W czystym tmuxie dowiadujesz się tego, odwiedzając każdy panel. Kosztem nie są naciśnięcia klawiszy — kosztem jest to, że zablokowany agent spalił jedenaście minut czasu zegarowego, nie robiąc nic, a ty nie miałeś jak o tym wiedzieć.

herdr to multiplekser terminala, który wie, czym jest agent kodujący. Te same prawdziwe panele PTY co w tmuxie, ten sam trwały serwer, ale każdy panel jest klasyfikowany jako idle, working albo blocked i pogrupowany w pasku bocznym. A pod spodem TUI siedzi socket API, które okazuje się ciekawszą połową: to prymityw dla agenta, który odpala i nadzoruje inne agenty.

  • Uruchomionej floty herdr i uczciwej historii tego, jak działa detekcja stanu
  • Dlaczego instalacja lifecycle hooków agenta zmienia blocked z heurystyki w fakt
  • CLI herdr agent: start, prompt, wait, read, send-keys — wszystkiego, czego potrzebuje skrypt
  • Działającego orkiestratora: jednego agenta, który rozdziela pracę workerom, czeka na nich i zbiera wyniki
  • Gdzie herdr się kończy i co nadal musisz posiadać sam (izolacja, review, koszt)

herdr to pojedynczy binarny plik w Rust, około 10 MB, na licencji Apache-2.0. Bez Electrona, bez konta, bez telemetrii — twój kod zostaje na twojej maszynie.

Okno terminala
curl -fsSL https://herdr.dev/install.sh | sh
# albo
brew install herdr
# albo
mise use -g herdr

Linux i macOS są stabilne; Windows to preview beta. Potem po prostu odpalasz i uruchamiasz agenty tak, jak już to robisz:

Okno terminala
herdr

Model interakcji jest mouse-first — prawy przycisk dzieli i przełącza — z ctrl+b jako prefiksem klawiaturowym, więc pamięć mięśniowa z tmuxa w większości się przenosi. To, czego w tmuxie nie ma, to pasek boczny: każdy panel pogrupowany po stanie agenta, więc „kto mnie potrzebuje” jest rzutem oka, a nie obchodem.

To jest funkcja, dla której adoptujesz herdr, więc warto wiedzieć dokładnie, co robi.

herdr klasyfikuje każdy panel jako idle, working albo blocked, jednym z dwóch mechanizmów, w zależności od agenta:

  • Lifecycle hooks. Tam, gdzie agent je wspiera, herdr je instaluje i agent sam raportuje swoje przejścia. To jest dokładne.
  • Manifesty ekranu. W przeciwnym razie herdr czyta wyrenderowany terminal. Dokumentacja jest odświeżająco precyzyjna co do ograniczenia: herdr oznacza panel jako blocked tylko wtedy, gdy snapshot dolnej części bufora pasuje do znanego, widocznego UI zgody, pytania lub uprawnień.

Przeczytaj ten drugi punkt jeszcze raz, bo definiuje tryb awarii. Detekcja przez manifest ekranu rozpoznaje znane kształty promptów. Nowy dialog uprawnień, własny Notification dodany przez twój zespół, agent zadający otwarte pytanie w nieznanym formacie — żadne z tych nie musi się zarejestrować. Panel dalej czyta się jako working, podczas gdy czeka na ciebie.

Pokrycie różni się między agentami i to najbardziej użyteczna tabela do planowania floty:

Poziom wsparciaAgenty
Pełne lifecycle hooksPi, OMP, Kimi Code CLI, Hermes Agent, MastraCode
Częściowe lifecycleClaude Code, Codex, Cursor Agent CLI, GitHub Copilot CLI, Devin CLI, Droid, OpenCode, Kilo Code CLI, Qoder CLI
Tylko manifest ekranuAmp, Grok CLI, Antigravity CLI, Kiro CLI, Maki
Mniej przetestowaneGemini CLI, Cline

Trzy agenty, na których zależy temu przewodnikowi, siedzą w warstwie częściowej — to dobre miejsce, ale nie najwyższe. Dowolny agent terminalowy działa i tak jako zwykły panel; dostajesz po prostu mniej sygnału.

herdr prowadzi serwer w tle, więc panele przeżywają odłączenie, a podłączyć się możesz z dowolnego miejsca:

  1. Odłącz się i odejdź. Agenty pracują dalej. To ta sama gwarancja, którą daje tmux, i powód, dla którego multiplekser terminala bije aplikację desktopową przy długich biegach.

  2. Postaw serwer tam, gdzie może stać. Mały VPS albo zapasowa maszyna oznaczają, że zamknięcie klapy nic nie zatrzymuje: ssh box -t herdr i flota jest z powrotem.

  3. Zaglądaj z telefonu. Układ reaguje na mysz i dotyk po SSH, co brzmi jak gadżet, dopóki agent nie zablokuje się, gdy jesteś na mieście, i nie odblokujesz go z pociągu.

  4. Przywracaj po reboocie świadomie. Eksport i aplikacja układu w herdr przywracają strukturę paneli. Nie przywracają rozmów agentów — połącz to z resume samego agenta (claude --resume, codex resume).

Tu herdr odchodzi od tmuxa. Zamiast wpisywać do paneli i grepować ich wyjście, adresujesz agenty po nazwie i czekasz na stany semantyczne.

Okno terminala
# uruchom nazwanego agenta w konkretnym panelu
herdr agent start reviewer --kind codex --pane "$review_pane"
# nadaj nazwę agentowi, który już działa
herdr agent rename w1:p2 reviewer
# wyślij prompt i blokuj do zakończenia
herdr agent prompt reviewer "Przejrzyj aktualny diff pod kątem pominiętych ścieżek błędów" \
--wait --timeout 120000
# blokuj do osiągnięcia stanu
herdr agent wait reviewer --until blocked --timeout 120000
# przeczytaj, co wyprodukował
herdr agent read reviewer --source recent-unwrapped --lines 120
# wyślij klawisze UI (escape, strzałki, akordy control) zamiast tekstu
herdr agent send-keys reviewer esc

Nazwy agentów podlegają wzorcowi [a-z][a-z0-9_-]{0,31}, a --kind przyjmuje rozpoznawane agenty — claude, codex, cursor, pi, gemini, devin, cline i inne. Wszystko po -- idzie do własnego CLI agenta, i tak przypinasz model albo tryb uprawnień per worker.

Dwa szczegóły warte zapamiętania. --until przyjmuje stany cyklu życia (idle, done, blocked, unknown), więc skrypt potrafi odróżnić „skończył” od „potrzebuje człowieka” bez parsowania czegokolwiek. A --source recent-unwrapped ma znaczenie z tego samego powodu, z którego liczy się capture-pane -J w tmuxie: niezawijane wyjście trzyma długie ścieżki plików i ramki stosu w jednej linii, więc twój grep faktycznie trafia.

Do zwykłej pracy w shellu są odpowiedniki na poziomie paneli:

Okno terminala
herdr pane run w1:p3 "just test --watch"
herdr pane wait-output w1:p3 --regex "passed|failed" --timeout 120000
herdr pane read w1:p1 --source recent --lines 50

Ponieważ CLI jest skryptowalne i świadome stanu, agent może prowadzić herdr — co oznacza, że jeden agent może zbudować flotę, dać jej prompty, poczekać na nią i przeczytać wyniki. To ta rzecz, którą herdr robi, a której prawie nic innego w tej kategorii nie wystawia.

#!/usr/bin/env bash
# bin/fanout -- jeden worker na zadanie, nadzorowany, wyniki zebrane
set -euo pipefail
TASKS=("api-rate-limit" "webhook-idempotency" "cache-invalidation")
BASE=$(herdr pane list --json | jq -r '.panes[0].pane_id')
WORKERS=()
# Nazwy agentów w herdr muszą pasować do [a-z][a-z0-9_-]{0,31}. Wyznacz nazwę RAZ
# i adresuj agenta po niej wszędzie; surowy slug zadania nie jest poprawnym
# uchwytem. Prefiks "w-" gwarantuje wymaganą pierwszą literę.
agent_name() {
printf 'w-%s' "$1" | tr '[:upper:]' '[:lower:]' | tr -c 'a-z0-9_-' '-' | cut -c1-32
}
# 1. zbuduj układ i uruchom jednego workera na zadanie
for task in "${TASKS[@]}"; do
name=$(agent_name "$task")
WORKERS+=("$name")
pane=$(herdr pane split "$BASE" --direction right --json | jq -r '.pane_id')
herdr agent start "$name" --kind claude --pane "$pane" -- --worktree "$task"
done
# 2. daj prompt każdemu workerowi, nie blokując się na żadnym
for i in "${!TASKS[@]}"; do
herdr agent prompt "${WORKERS[$i]}" \
"Posiadasz dokładnie jedno zadanie: ${TASKS[$i]}. Zostań w swoim worktree.
Kiedy testy przechodzą, zacommituj na swoim branchu i zatrzymaj się. Nie
dotykaj innych obszarów."
done
# 3. nadzoruj: zaklasyfikuj każdego workera jako done, blocked albo timeout
for i in "${!TASKS[@]}"; do
name="${WORKERS[$i]}"
if herdr agent wait "$name" --until done --timeout 1800000; then
echo "== ${TASKS[$i]} DONE =="
herdr agent read "$name" --source recent-unwrapped --lines 60
elif herdr agent wait "$name" --until blocked --timeout 1000; then
# już jest zablokowany, więc to drugie czekanie wraca natychmiast
echo "!! ${TASKS[$i]} BLOCKED -- czeka na człowieka"
herdr agent read "$name" --source recent-unwrapped --lines 30
else
echo "!! ${TASKS[$i]} TIMEOUT -- nadal pracuje po 30 minutach"
herdr agent read "$name" --source recent-unwrapped --lines 30
fi
done

Zwróć uwagę, czego skrypt nie robi: nigdy nie tworzy worktree. -- --worktree "$task" przepuszcza flagę do Claude Code, który posiada własną izolację. herdr posiada panele i stan; agent posiada system plików. Utrzymanie tej linii w czystości jest tym, co powstrzymuje obie warstwy od walki.

Drugi szczegół wart skopiowania to obejście przez agent_name. Slug zadania nie jest uchwytem agenta — nazwy w herdr muszą pasować do [a-z][a-z0-9_-]{0,31}, więc cokolwiek z wielką literą, kropką albo ukośnikiem zostaje po drodze przekształcone. Wyznacz nazwę raz, trzymaj ją w równoległej tablicy i adresuj każdy późniejszy prompt / wait / read po tej nazwie. Sanityzuj przy start, a potem adresuj po surowym slugu i dostaniesz najgorszą możliwą awarię: worker działa sobie szczęśliwie pod sanityzowaną nazwą, a twoje prompty i czekania po cichu celują w agenta, który nie istnieje.

CLI jest klientem lokalnego socket API i możesz z nim rozmawiać wprost, gdy potrzebujesz subskrypcji zdarzeń albo chcesz prowadzić herdr z czegoś innego niż bash.

Transportem jest JSON rozdzielany nowymi liniami po unixowym sockecie (nazwane potoki na Windowsie). Jedno żądanie na linię, odpowiedzi dopasowane po ID żądania:

{"id":"req_1","method":"ping","params":{}}
{"id":"req_1","result":{"type":"pong"}}

Kolejność rozwiązywania ścieżki socketu: jawne --session <nazwa>, potem HERDR_SOCKET_PATH, potem HERDR_SESSION=<nazwa>, potem domyślne ~/.config/herdr/herdr.sock.

Metody są pogrupowane po domenach:

DomenaOperacje
workspacecreate, list, focus, rename, close
tabcreate, list, focus, rename, move, close
panesplit, swap, focus, resize, read, send input, close
agentlist, read, prompt, wait, start, focus
layoutexport, apply, ustawianie proporcji podziału
eventssubscribe, oczekiwanie na zmiany wyjścia
{"id":"req_1","method":"pane.split","params":{"pane_id":"w1:p1","direction":"right","ratio":0.333}}
{"id":"req_2","method":"pane.run","params":{"pane_id":"w1:p2","command":["npm","test"]}}

Domena events jest tą, po którą warto sięgnąć: subskrybujesz na długotrwałym połączeniu i zmiany stanu są ci wypychane, zamiast żebyś odpytywał. To różnica między orkiestratorem reagującym w sekundę i takim, który reaguje na kolejnym 15-sekundowym tiku.

Zanim cokolwiek na tym zbudujesz, wygeneruj kontrakt, zamiast zgadywać:

Okno terminala
herdr api schema --json

herdr wersjonuje protokół, więc klient powinien sprawdzić możliwości serwera przed poleganiem na nowej metodzie i tolerować nieznane pola, a nie wywalać się na nich.

--kind claude, a izolację przepuszczaj dalej: -- --worktree <zadanie> umieszcza workera w .claude/worktrees/<zadanie>/ na własnym branchu. Uruchomienia interaktywne wymagają najpierw workspace trust, więc odpal raz claude w repo ręcznie, zanim to oskryptujesz.

Własne hooki Claude Code są tym, co podnosi go ze zgadywania z manifestu ekranu do prawdziwego sygnału, więc zainstaluj dla niego hooki herdr. Jeśli twój zespół używa hooków też do innych celów, trzymaj je addytywnie — hook Stop, który kończy się niezerowym kodem, objawi się jako błąd przy każdym zatrzymaniu.

Zaufałeś working u agenta, który faktycznie był zablokowany. Detekcja przez manifest ekranu rozpoznaje tylko znane kształty promptów. Zainstaluj lifecycle hooks, a workera, który jest working bez wyjścia od dłuższego czasu, traktuj jako podejrzanego, nie zajętego.

Twój orkiestrator wisi. Czekał na done u workera, który poszedł w blocked. Zawsze podawaj --timeout, zawsze obsługuj niezerowy kod wyjścia i wypisuj wtedy ostatnie wyjście workera.

prompt albo wait celuje w agenta, który nie istnieje. Sanityzowałeś nazwę przy agent start, a potem adresowałeś surowy slug zadania. Nazwy w herdr muszą pasować do [a-z][a-z0-9_-]{0,31}, więc wyznacz uchwyt raz i używaj go ponownie — worker działa sobie spokojnie pod swoją prawdziwą nazwą, a twoje komendy po cichu nie trafiają nigdzie.

Dwie warstwy pobiły się o worktree. herdr nie zarządza izolacją i nie należy go do tego zmuszać. Niech agent utworzy swój worktree własną flagą; zadanie herdr kończy się na panelu.

Porty, .env i baza dev i tak się zderzyły. Worktree izoluje wyłącznie pliki. Sześciu workerów odpalających npm run dev bije się o port 3000 niezależnie od tego, jak czysty jest twój multiplekser. Najpierw zaudytuj repo — gotowy prompt audytowy jest w alternatywnych IDE i powłokach dla agentów — albo użyj izolacji kontener-na-agenta.

Zbudowałeś na metodach, których nie ma. Socket API jest wersjonowane i się rozwija. Uruchom herdr api schema --json i buduj z wyjścia, a nie z przykładu na blogu — w tym z przykładów w tym artykule, które są aktualne na lipiec 2026 i będą się rozjeżdżać.

Flotę zatrzymały limity, nie herdr. Każdy panel wydaje przydział tego samego konta, a worker, który uderzył w limit, często wygląda identycznie jak ten, który myśli. Sprawdź panel, zanim zaczniesz debugować harness.

Flota wyprzedziła twoje review. Pięciu workerów produkujących pięć branchy produkuje pięć review, wszystkie przychodzące naraz. Liczbą, która się liczy, jest zaakceptowana przepustowość — kalibracja sufitu w równoległości zespołowej, a strona tokenowa w optymalizacji kosztów.

Windows zachował się inaczej. Wsparcie dla Windowsa to preview beta i używa nazwanych potoków, a nie unixowych socketów. Traktuj tam skryptowaną orkiestrację jako eksperymentalną.