Pouch β store instructions for LLMs while they work in Herdr
A Herdr plugin for stashing prompts before an agent needs them, and inserting them when it is ready.
Queue the next three instructions while the agent is still working, then hand them over one keypress at a time. Text is typed into the input, never submitted β you always press Enter yourself.
β 1 π β¬ 2 β¬ + ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β > implement the retry logic β
Requires Herdr 0.7+ and Node 22.6+ or Bun . No build step, no clone.
herdr plugin install AltanS/herdr-pouch herdr plugin action invoke setup --plugin herdr.pouch
Expected setup output:
β plugin installed from GitHub, nothing to link
β cli installed ~/.local/bin/herdr-pouch
β keys prefix+shift+o, prefix+shift+i, prefix+shift+a bound
β keys work prefix+shift+o, prefix+shift+i, prefix+shift+a reach Pouch
β did it. Β· nothing to do. ! needs you, with the remedy on the same line. Re-running setup is
safe, and it is how you repair an install.
Setup writes one marked block to ~/.config/herdr/config.toml, between # pouch:begin keybindings
and # pouch:end keybindings. Anything outside those markers is yours, and is never rewritten β a
chord you already use is reported and left alone. Uninstall removes exactly that block. Setup also
keeps a backup at ~/.config/herdr/config.toml.pouch-backup, and uninstall leaves it there.
setup --no-keys skips the config entirely, so you can bind herdr.pouch.<action> yourself.
Check it worked: herdr-pouch add "run the tests" in any pane. The pane wears a π, and
prefix+shift+O opens the pouch. Until a pouch holds a message, Pouch shows nothing β that is
normal, not a failed load.
herdr plugin action invoke update --plugin herdr.pouch # routine update herdr plugin action invoke update-major --plugin herdr.pouch # crossing a major herdr plugin action invoke uninstall --plugin herdr.pouch
herdr-pouch update and herdr-pouch uninstall do the same from a shell. Update advances the
checkout and re-links it β Herdr caches the action set at link time, so a release that adds an action
answers plugin_action_not_found until it is linked again.
Uninstall keeps your stashed messages. They are your writing, not a cache. --purge deletes them
too, --dry-run shows what would go, and --keep-config leaves config.toml alone.
Installs older than 0.3.0 have no update action: cd <checkout> && git pull --ff-only && herdr plugin link "$PWD".
Stash from anywhere β a shell, a script, or one agent preparing work for another:
herdr-pouch add "run the full test suite and report only failures" herdr-pouch add --pane w1:p3 "now write the changelog entry" git log -1 --format=%B | herdr-pouch add --agent reviewer
| key | does |
|---|---|
prefix+shift+O |
open the pouch for the focused pane |
prefix+shift+I |
insert the top message |
prefix+shift+A |
write a new message in the editor |
Insert-and-submit ships unbound β every free chord was taken. Bind
herdr.pouch.insert-top-submit yourself, or invoke it from the action menu.
Inside the popup:
| key | effect |
|---|---|
ββ / jk |
move Β· g/G jump to ends |
1β9 |
insert that slot and close |
enter |
insert the selected message and close |
space / i |
insert without closing β for queueing several |
/ |
filter as you type; enter keeps it, esc clears it |
y |
copy to the clipboard (OSC 52) |
n Β· e Β· d |
new Β· edit Β· delete |
J / K |
reorder (clear the filter first) |
p |
switch pouch β every pouch on disk, live agent or not |
I |
choose which pane inserts go to |
q / esc |
close |
Clicking a row selects it; clicking it again inserts.
| surface | scope | works when |
|---|---|---|
| pane border, a π | that pane | the pane shares a tab with another |
| tab label | that tab | the pane is alone in its tab and has no border |
agent sidebar, $pouch |
every pane | your rows name the token (see Config) |
| the strip | that pane | pinned with herdr-pouch strip β costs three rows |
A pouch outlives the pane it was filled for. When that agent is gone the pouch has no destination, so
I (or any insert) asks which pane to send to. p, herdr-pouch browse and the Pouch: browse all
pouches action are the only way back into an orphan.
- The pouch is empty β an empty pouch paints nothing, on purpose.
- A remote client kills all three keys. Herdr applies the foreground client's keybindings to
the whole server, and
herdr --remotesends a profile that drops every[[keys.command]]entry. Attach withherdr --remote <host> --remote-keybindings server, or use the action menu. herdr plugin action invoke doctor --plugin herdr.pouch, then readherdr plugin log list --plugin herdr.pouch. It says which case you are in.herdr server reload-configafter any config edit. Herdr does not hot-reloadconfig.toml.herdr plugin listconfirms the plugin is registered at all.
| command | does |
|---|---|
add [target] [text...] |
stash a message (reads stdin when no text) |
list [target] [--all] |
show a pouch, or every pouch with --all |
show <ref> [target] |
print one message in full |
insert [ref] [target] |
type it into the agent's input (--submit, --to <pane>) |
rm <ref>, clear |
remove a message, or empty the pouch |
strip, unstrip |
pin or remove the three-row strip under a pane |
open, browse |
the popup for one pouch, or the picker over all of them |
sync, key |
repaint the indicators; print the resolved pouch identity |
setup, update, uninstall |
install, advance, remove |
Targets β --here (the calling pane, default) Β· --pane w1:p3 Β· --agent <name> (a live agent
name, or a saved pouch label). Refs β a 1-based slot number, a message id, or a unique text
prefix.
~/.config/herdr/plugins/config/herdr.pouch/config.json. All keys optional.
| key | default | effect |
|---|---|---|
editor |
"builtin" |
what n and e open. "builtin" is Pouch's own box (ctrl-s saves, esc cancels); "system" is $VISUAL/$EDITOR; anything else runs as a command with the file appended |
consumeOnInsert |
false |
remove a message once it has been used |
focusAfterInsert |
true |
focus the agent pane after inserting, so Enter submits |
indicator |
"π" |
the mark on the border or tab label; {count} is replaced with the count |
maxChipWidth |
34 |
widest strip chip, in columns |
pollMs |
500 |
how often the strip re-reads the pouch |
POUCH_EDITOR, POUCH_CONSUME=1 and POUCH_NO_FOCUS=1 override the file for one run.
POUCH_STATE_DIR moves the store.
The count is also a $pouch token for the sidebar:
[ui.sidebar.agents] rows = [["state_icon", "workspace", "tab", "$pouch"], ["agent"]]
~/.local/state/herdr/plugins/herdr.pouch/ β one JSON file per pouch under pouches/, plus the
derived bookkeeping (aliases, pinned strips, tab marks, workspace labels) that sync rebuilds.
A pouch is keyed by (workspace label, cwd, agent), never by pane id: pane ids are not reused,
change on move, and are regenerated when the server restarts. So the pouch you filled yesterday is
still on the same agent this morning, and renaming a workspace migrates its pouches rather than
stranding them. Every write takes a lock and re-reads inside it, so the popup, the strip, the CLI and
Herdr's event hooks can never clobber each other.
git clone git@github.com:AltanS/herdr-pouch.git && bun install herdr plugin link "$PWD" # re-run after ANY manifest change bun run lint && bun x tsc --noEmit && bun run test
All three gates must pass. bun run test also runs the CLI under both Node and Bun, which is what
catches syntax that Node's type-stripping cannot handle. See CLAUDE.md for the
versioning rules and the Herdr API traps.
MIT Β© Altan Sarisin