Herdr peer review
Open a second coding agent in a herdr pane and have it review your diff — watchable, auto-approve, read-only. Ships a Claude Code skill for the autonomous review↔revise↔decide loop.
npx -y skills add Elio2000/herdr-peer-reviewAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- 22 days oldThe repository was created 22 days ago. New is not bad, but a brand new repository carrying a familiar-sounding name is the shape a typosquat arrives in, and there has been no time for anyone else to find a problem with it.
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Drive a second ("peer") coding agent from Claude inside a herdr session, in a watchable TUI pane, so the user can SEE the multi-agent conversation. Use when running inside herdr and the user asks to have another agent review the current changes / a diff / a file, or to consult / ask another agent ("让 codex/grok review", "问一下另一个 agent", "找 X 咨询一下", "peer review", "第二意见"). The peer agent is a single swappable config knob — codex today, grok / cursor / claude / etc. tomorrow. Completion is detected via herdr's agent-status (agent-agnostic). The peer is ALWAYS launched auto-approve ("YOLO") so the loop never stalls on a prompt and can run unattended / AFK; the peer can also arbitrate decisions to close the loop with no human present. Encodes the project's Claude = Code Agent / peer = Reviewer split.
SKILL.md
18.9 KB, ~4.9k tokens by cl100k_base, as published. Nobody here has run it
peer-agent-via-herdr
Open a peer coding agent (codex now; grok / cursor / claude / … later) in its own herdr pane and drive it from Claude over herdr's socket CLI, keeping the TUI visible so the user watches the exchange live. The peer is one config knob — nothing about "codex" is baked into the flow.
The generic, agent-independent backbone is: pane split → launch the agent → agent send +
Enter → wait agent-status … --status done → pane read. herdr's screen-manifest state detection
gives working/idle/done for common agents without any hook, so the loop is the same
regardless of which agent you run; only the launch command and the answer-line marker differ.
Verification status (herdr 0.7.1, macOS):
- Verified live with codex: the full loop below — launch,
agent send+send-keys entersubmit, autoworking→donedetection,agent read; plus that herdr injectsHERDR_ENV=1/HERDR_PANE_IDinto a pane's process (so an in-paneclaudeand its shells inherit them). - Designed-for, not yet verified per agent: grok / cursor / claude / others — the loop is generic but confirm each agent's launch command + answer marker on first use (see the config table).
- Driving herdr is shell-agnostic (fish and zsh both fine): in the TUI path prompts go to the
agent, not the shell, so shell syntax is irrelevant. Only the quiet path injects shell text.
Caveat: the
peer.shhelpers + the runnable recipe are bash — source them from bash (bash -c '. peer.sh; …'), not from fish. Shell-agnostic refers to the herdr commands, not peer.sh.
Configure the peer agent (the ONE place to change)
⚠️ Collaboration is ALWAYS launched auto-approve ("YOLO"). The entire point is running agents while the user is away from the keyboard — any interactive approval/choice prompt stalls the loop and defeats it. So every peer launch MUST include the agent's no-prompt flags; never launch a bare TUI that can block on a confirmation.
Auto-approve (no stall) and sandbox tightness are separate knobs. --ask-for-approval never
stops the stalls; the sandbox decides what the peer may touch. A pure REVIEWER needs no write
access — default it to --sandbox read-only (still never stalls, but can't edit files, run
scripts, or reach secrets). Widen to workspace-write only for a peer that must ACT, and use
--dangerously-bypass-approvals-and-sandbox only on a machine that is already externally sandboxed.
# AGENT_CMD = the FULL launch line, INCLUDING the agent's auto-approve flags (see table).
AGENT_CMD="codex --ask-for-approval never --sandbox read-only" # REVIEWER: auto-run, cannot write
AGENT_NAME="codex" # label herdr uses in `agent list` (usually the binary name)
# peer that must ACT (edit/patch): AGENT_CMD="codex --ask-for-approval never --sandbox workspace-write"
# externally-sandboxed box only: AGENT_CMD="codex --dangerously-bypass-approvals-and-sandbox"
| Agent | auto-approve ("YOLO") launch | herdr integration | answer marker | status |
|---|---|---|---|---|
| codex | codex --ask-for-approval never --sandbox read-only (act: --sandbox workspace-write; full: --dangerously-bypass-approvals-and-sandbox) | auto-detected¹ | • prefix | ✅ launch+detect verified |
| claude | claude --dangerously-skip-permissions (or --permission-mode bypassPermissions) | integration install claude | (verify) | flag verified |
| cursor | its CLI's force/auto flag — verify | integration install cursor | (verify) | template |
| grok | its CLI's auto flag, once released | maybe via cursor | (verify) | template |
¹ codex state is auto-detected — you don't need herdr integration install codex, and running it
can clobber an existing notify hook in ~/.codex/config.toml (e.g. one installed by another
tool). For other agents, herdr integration install <name> is fine and improves detection.
Preconditions
# NB: `herdr status server` exits 0 even when the server is down — it prints "status: not
# running". Match the OUTPUT; an exit-code check silently passes and every later call then
# dies with "Connection refused".
herdr status server 2>/dev/null | grep -q "^status: running" || { echo "herdr server not running — skill N/A"; exit 1; }
[ "${HERDR_ENV-}" = 1 ] || echo "warning: HERDR_ENV≠1 (not launched inside a herdr pane); proceeding via socket" # ${HERDR_ENV-} is set -u safe
command -v "${AGENT_CMD%% *}" >/dev/null || { echo "${AGENT_CMD%% *} not on PATH"; exit 1; } # first word only — AGENT_CMD includes flags
Spawn a visible peer pane next to Claude
# base pane = Claude's own pane if inside herdr, else the focused pane
BASE="${HERDR_PANE_ID:-$(herdr pane current 2>/dev/null \
| python3 -c 'import sys,json;print(json.load(sys.stdin)["result"]["pane"]["pane_id"])')}"
[ -n "$BASE" ] || { echo "no base pane resolvable"; exit 1; }
Q=$(herdr pane split "$BASE" --direction right --no-focus \
| python3 -c 'import sys,json;print(json.load(sys.stdin)["result"]["pane"]["pane_id"])')
echo "peer pane = $Q" # sits beside Claude; --no-focus keeps you here but both stay visible
Alternative — give the peer its own tab (cleaner isolation; the user flips to it to watch the
whole conversation). tab create returns the id at result.root_pane.pane_id (verified):
Q=$(herdr tab create --workspace "${HERDR_WORKSPACE_ID:-$(herdr pane current | python3 -c 'import sys,json;print(json.load(sys.stdin)["result"]["pane"]["workspace_id"])')}" \
--label peer --no-focus \
| python3 -c 'import sys,json;print(json.load(sys.stdin)["result"]["root_pane"]["pane_id"])')
Primary flow — interactive TUI (watchable)
herdr pane run "$Q" "$AGENT_CMD" # launch the peer agent's TUI
herdr wait agent-status "$Q" --status idle --timeout 30000 # composer ready
# send a focused review/consult prompt (Claude = Code, peer = Reviewer):
herdr agent send "$Q" "Review the current git diff for correctness bugs, data-routing errors and eval regressions. Report [severity] file:line — issue — fix. Separate confirmed bugs from risks."
herdr pane send-keys "$Q" enter # submit (send writes literal text, no Enter)
herdr wait agent-status "$Q" --status working --timeout 20000 # best-effort: confirm it picked up
herdr wait agent-status "$Q" --status done --timeout 900000 # FINISHED (the UI's 'done'; agent-agnostic)
herdr agent read "$Q" --source recent-unwrapped --lines 300 # reading the pane is what acknowledges 'done'
Prefer the hardened wrapper. The commands above are the teaching version; in scripts use
peer_launch/peer_askfrompeer.sh. They make the waits fatal — a barewait agent-status … --status donehangs for the entire timeout if the peer exits (goes undetected) instead of reachingdone(observed live in the dogfood) — detect a vanished peer, and return answer-only text (stripping the echoed prompt so downstreamCHOICE=/VERDICT=parsing can't false-match your own prompt).
Why
wait agent-status, notagent wait:wait agent-statusis herdr's canonical waiter and the only one whose--statusacceptsdone;agent waitaccepts only idle/working/blocked. Semantics (per the official skill):done= the agent finished but you haven't looked yet —agent readis the "looked at it" step. This is the agent-agnostic completion signal (verified for codex; works for any agent herdr detects).
- Multi-turn consult ("咨询"): keep the pane open and just send again —
herdr agent send "$Q" "<follow-up>"; herdr pane send-keys "$Q" enter; herdr wait agent-status "$Q" --status done …. The user watches the whole back-and-forth in the pane; Claude relays a summary into this session. - Reading the reply:
--format text(default) strips ANSI, so the buffer is plain text with light TUI framing. For codex, the agent's answer lines are prefixed•and your prompt echoes with›; grep those. For a new agent, eyeball the buffer once and note its marker in the table. If plain text loses structure you need (a TUI feedback loop), read a rendered snapshot instead:herdr agent read "$Q" --format ansi(orpane read … --format ansi).
If herdr can't auto-detect the agent's state
Some agents may not report working/idle (no integration + unrecognized screen). Then:
herdr integration install <AGENT_NAME> (improves detection), or fall back to matching the
agent's own on-screen "done" cue: herdr wait output "$Q" --match '<per-agent marker>' --source recent --timeout …
(match against --source recent, i.e. unwrapped text, so narrow-pane soft-wrapping can't break the match).
Close the loop AFK — delegate the decision, not just the review
The peer can do more than review: when Claude hits a choice point (Claude produced a plan with options A/B/C), hand the decision to the peer so the flow closes with no human present. Frame it as a contract with a machine-parseable answer:
Use the peer_decide helper (peer.sh) — do NOT hand-roll this. The footgun: the prompt itself
contains CHOICE=…, and the pane echoes the prompt, so a naïve grep CHOICE= over the buffer
matches your own prompt, not the peer's answer. peer_decide (and peer_ask) prepend a unique
marker and parse only the text after the last echoed marker line:
pick=$(peer_decide "$Q" "Context: <plan + tradeoffs>. Options: A) … B) … C) …. Choose exactly one.")
# pick is the bare token (e.g. A). Returns nonzero if the peer gave no CHOICE= line — handle that.
If you must do it inline, slice off the echoed prompt before parsing (send with a marker, read
pane read --source recent-unwrapped, take lines after the last marker, then grep -oE 'CHOICE=[A-Za-z0-9_.-]+' | tail -1).
Claude then proceeds with the chosen option: Claude writes → peer reviews and arbitrates → Claude continues, no human in the loop. For a fully unattended run:
- Both sides autonomous: the human launches the driving Claude in bypass too
(
claude --dangerously-skip-permissions); the peer is already YOLO (config above). - Contain the blast radius: run the loop in a throwaway git worktree (
herdr worktree …) so a bad auto-decision can't corrupt the main tree. - Bound it: cap review↔revise rounds (e.g. ≤ 3) and define a done condition (tests green, or the
peer replies
LGTM) so the two agents can't ping-pong forever. - Escalate the irreversible — don't YOLO it: for one-way doors (
git push, deploy,rm -rf, spending money, external network writes) the loop should STOP and page the human instead of auto-approving. That is exactly what the herdr-remote plugin is for (approve from your phone). Everything reversible runs YOLO; only the irreversible waits for you.
Runnable recipe — autonomous review→revise→decide loop (AFK)
Mechanical helpers live in peer.sh next to this file (peer_launch / peer_ask /
peer_decide / peer_escalate / peer_close; every herdr call in it is verified). Claude's own
coding turns interleave with them. This closes the loop with no human — unless an irreversible action
needs sign-off, which pages you instead.
. ~/.claude/skills/peer-agent-via-herdr/peer.sh # bash only; PEER_AGENT_CMD defaults to codex auto-approve + read-only (reviewer)
# 0) isolate: a throwaway worktree so a bad auto-decision can't touch main
WT="$(mktemp -d)/wt"; git worktree add "$WT" HEAD
# 1) launch the peer (YOLO), pointed at the worktree so it reviews the right tree
Q=$(peer_launch "$WT")
# 2) review ↔ revise, capped at 3 rounds (done-condition = peer says LGTM)
for round in 1 2 3; do
# ← Claude edits code in "$WT" here (Claude's own turn) →
review=$(peer_ask "$Q" "Review the uncommitted changes in this repo for correctness, data-routing and eval-regression bugs. FIRST line must be exactly VERDICT=CLEAN or VERDICT=CHANGES; if CHANGES, list [severity] file:line — issue — fix.") || { echo "peer_ask failed: $?"; break; }
# ← Claude reads $review and addresses the findings →
# $review is answer-only (peer_ask strips the echoed prompt), so this can't match the prompt text:
printf '%s\n' "$review" | grep -q '^VERDICT=CLEAN' && { echo "peer approved (round $round)"; break; }
done
# 3) decision point → let the peer ARBITRATE (no human)
pick=$(peer_decide "$Q" "Choose the rollout for this change: A) merge to main now B) open a PR C) keep on the branch.")
echo "peer chose: $pick"
# 4) irreversible action → escalate instead of YOLO-ing it
case "$pick" in
A) if ! peer_escalate "$Q" "peer chose to merge $WT into main — approve the push?"; then
echo "HALTED for human approval — leaving pane $Q + worktree $WT in place; do NOT clean up"
exit 0 # STOP before cleanup; resume after you approve on your phone / take over the pane
fi ;;
*) echo "reversible ($pick) — proceeding autonomously" ;;
esac
# 5) cleanup — only reached when nothing is pending human approval
peer_close "$Q"; git worktree remove "$WT" --force
Guardrails are structural, not optional: worktree isolation (0), round cap + LGTM done-condition
(2), peer-arbitrated decision (3), escalate-not-YOLO for the one-way door (4). For a fully unattended
run the human also launches the driving Claude with --dangerously-skip-permissions, and (for the
phone page in step 4 to reach them) has the herdr-remote relay + herdr-push plugin configured.
Robust one-shot review — codex exec in a visible pane (recommended for reviews)
For a one-shot review (the common "让 codex review 一下" ask), prefer this over the TUI-paste
path: the prompt goes as a CLI argument (no fragile paste into a composer that can mangle a long
prompt or make the agent exit), and it's still visible — the user watches codex exec stream in the
pane. Use the peer_pane + peer_exec helpers (the caller owns the pane, so its id survives
$(...)):
. ~/.claude/skills/peer-agent-via-herdr/peer.sh # bash only
Q=$(peer_pane "$REPO_OR_WORKTREE") # fresh bare pane (create it yourself, then close it)
review=$(peer_exec "$Q" "Review the uncommitted changes for correctness/data-routing/eval-regression bugs; report [severity] file:line — issue — fix.")
peer_close "$Q"
Two footguns this design avoids (both hit live in dogfooding):
- Long prompt —
peer_execdelivers the prompt via a stdin file (codex exec - < file), not on the command line; a long prompt on thepane runcommand line gets truncated and codex never runs. - Completion — it polls
pane process-infountil thecodexprocess leaves the foreground. Do notwait output --match 'tokens used': a review's own text can contain "tokens used" so the waiter matches the review body, not codex's footer. (codex execalso isn't reliably agent-detected — flickersidlethen vanishes — sowait agent-statusdoesn't work for it either.)
Launch in a git repo (codex trusts it; avoids a trust-prompt / early exit). Other agents may have
no exec equivalent — use the TUI flow.
Golden rules (footguns hit live — do not skip)
- Re-query IDs every time; never cache. Panes and whole workspaces churn mid-session
(observed
w1→w2; a pane wentpane_not_found). IDs look likew2:p3/w2:t1, not1-1. - A freshly split pane is NOT ready —
pane runtypes into whatever is there.pane splitreturns before the pane's login shell is usable, andpane runsends text blind. Two live failures: (a) the shell is still running rc subprocesses (sw_vers, from oh-my-zsh's startup) —pane process-infoshows the subprocess as foreground, and your text goes to it; (b) an rc that asks something ([oh-my-zsh] Would you like to update? [Y/n]) eats the first characters —codex exec …becameodex exec …→command not found, and the review silently never ran.pane runalso doesn't clear a stale/errored line, which concatenates into your command. So always: pollpane process-infountil the foreground argv0 is the shell itself, thensend-keys ctrl+c(dismiss a prompt) +ctrl+u(clear the line), and only thenpane run.peer.sh's_run_cleandoes exactly this — usepeer_launch/peer_execand you get it free. - Any shell text you send must appear only in OUTPUT if you match on it, and be valid in the
pane's shell. Only the quiet path sends shell text. The pane runs the user's login shell —
fish needs
(math "6 x 7"), zsh/bash need$((6*7)); checkecho $SHELLand pick accordingly. The TUI flow avoids this entirely (prompts go to the agent). - Keep the peer pane open while the user wants to watch/take over.
herdr agent attach "$Q" --takeoverlets them jump in; the pane survives laptop-close / ssh — that persistence + live visibility is the whole reason to use this over an in-band review.
Cleanup
Leave the pane open for the user to read/continue. When truly done, quit the agent then close:
herdr pane send-keys "$Q" ctrl+c # twice if needed, or the agent's own /quit + enter
herdr pane close "$Q"
Notes
- Thin specialization over the official general herdr skill (herdr ships one at
SKILL.md; install it alongside this one) — lean on it for the general primitive reference: the full command set, the--source recent(unwrapped) vsrecent-unwrappedmatching semantics,--format ansisnapshots, and the JSON id contract (pane split → result.pane.pane_id,tab create → result.tab+result.root_pane,workspace create → result.workspace/tab/root_pane). This skill only adds the peer-review/consult workflow + config table + role split; don't re-teach primitives. - Role convention: Claude = Code Agent, peer = Reviewer. Feed the peer focused review/consult
prompts and relay its findings — don't have it edit files. Pin this in your project's
CLAUDE.md/AGENTS.mdso the split survives across sessions.
What ships with it: 7 files
23.8 KB alongside SKILL.md, 4 of them executable
scripts/
- common.shruns4.2 KB
- consult.shruns1.1 KB
- review.shruns2.1 KB
- herdr-plugin.toml1.2 KB
- LICENSE1.0 KB
- peer.shruns9.2 KB
- README.md4.9 KB