Loop prompts into tmux panes with triggers, delays, and branching rules. Built to automate iterative workflows for code assistants running in tmux (OpenCode, Codex, Claude Code).
loopmux watches tmux output and injects prompts when a trigger matches. You can chain flows, add pre/post blocks, and control delays so your iterations feel deliberate instead of spammy.
- YAML config with
pre,prompt,postblocks - Ordered rule evaluation with
first_match,priority, ormulti_match - Include/exclude match criteria (regex/contains/starts_with)
- Delay strategies: fixed, range, jitter, backoff
- Mid-flight loop runner (tmux capture + send)
- Structured logging (text or JSONL)
loopmux is tmux-first and backend-agnostic. If your assistant runs in a tmux pane, loopmux can target it.
Example tmux targets:
- OpenCode:
ai:5.0 - Codex:
codex:1.0 - Claude Code:
claude:2.0
brew tap dmoliveira/tap
brew install loopmuxgit clone https://github.com/dmoliveira/loopmux.git
cd loopmux
cargo build --release
./target/release/loopmux --help- Create a config:
loopmux init --output loop.yaml-
Update the tmux target and rules in
loop.yaml. -
Validate config:
loopmux validate --config loop.yaml- Run the loop:
loopmux run --config loop.yamlloopmux run -t ai:5.0 -n 5 \
--prompt "Do the next iteration." \
--trigger "Concluded|What is next" \
--oncetarget: "ai:5.0"
iterations: 10
default_action:
prompt: "Do the next iteration."target: "ai:5.0"
iterations: 50
trigger_confirm_seconds: 5
recheck_before_send: true
rule_eval: first_match
template_vars:
project: loopmux
default_action:
pre: "Keep context on UX simplification."
prompt: "Do the next iteration for {{project}}."
post: "Run lint/tests; fix failures."
delay:
mode: range
min: 5
max: 120
rules:
- id: success-path
match:
regex: "(All tests passed|LGTM)"
exclude:
regex: "PROD"
action:
prompt: "Continue with next iteration."
next: review-path
- id: review-path
match:
regex: "(Ready for review|PR created)"
confirm_seconds: 3
delay:
mode: fixed
value: 300
action:
prompt: "Audit UX for simplification."
next: success-path
- id: failure-path
match:
regex: "(FAIL|Error|Exception)"
action:
pre: "Fix the errors before proceeding."
prompt: "Repair and re-run tests."
post: "Summarize fixes."
next: success-path
logging:
path: "loopmux.log"
format: "jsonl"examples/loopmux.example.yamlexamples/loopmux.lean.yaml
first_match: ordered rules; first match wins.multi_match: all matching rules fire in order.priority: highest priority wins (ties resolved by order).
fixed: static delay in seconds.range: random delay betweenminandmax.jitter: range plus +/- jitter factor (0.0..1.0).backoff: exponential backoff usingbase,factor,max.
loopmux run --config loop.yaml [--target ai:5.0] [--iterations 10]
loopmux run --config loop.yaml --dry-run
loopmux validate --config loop.yaml [--skip-tmux]
loopmux init --output loop.yaml
loopmux runs ls
loopmux runs tui
loopmux runs stop <run-id-or-name>
Use inline flags to run a quick loop without a config file.
loopmux run -t ai:5.0 -n 5 \
--prompt "Do the next iteration." \
--trigger "Concluded|What is next" \
--exclude "PROD" \
--once--prompt: required prompt body.--trigger: regex to match tmux output (required).--trigger-exact-line: treat--triggeras an exact trimmed line match (good for sentinel tokens like<CONTINUE-LOOP>).--exclude: regex to skip matches (optional).--pre/--post: optional prompt blocks.--once: send a single prompt and exit.--tail N: number of capture-pane lines (default 1, last non-blank line).--single-line: update status output on a single line.--poll N: polling interval in seconds while waiting for matches (default 5).--trigger-confirm-seconds N: require trigger to stay matched for N seconds before send (default 5).--log-preview-lines N: number of captured lines shown in folded sent-log previews (default 3).--no-trigger-edge: opt out of edge-guard (default guard is ON to avoid repeated queue injections while trigger stays true).--no-recheck-before-send: skip the default pre-send trigger recheck (default is ON).--fanout matched|broadcast: send to matched panes only (default) or broadcast to all panes in scope.--tui: enable the interactive terminal UI.--history-limit N: max history entries to keep/show in TUI picker (default 50).--name: optional codename for this run; auto-generated if omitted.
- Run
loopmux run --tuiwith no prompt/config to pick from recent commands. - Entries are stored in
~/.loopmux/history.json, newest first, deduplicated by command shape. - TUI controls:
hhold/resume (non-consuming, aliasp/r),fopen fleet manager view,Rrenew counter,nnext,s/Ctrl+Cstop run,qquit run view. - When
--durationis set, the TUI status bar shows remaining time (rem ...) and it freezes while HOLD is active. - Run TUI status bar includes current loopmux version (
vX.Y.Z) for quick parity checks. - Sent logs are compact and include a folded trigger preview (
Nlines from capture tail) to keep long prompts readable. - TUI log timestamps use subtle date-aware coloring to make same-day activity easier to scan.
- Every running
loopmux runwrites a local registry entry under~/.loopmux/runs/state/. - Each run has an id plus a codename (
--nameor auto-generated likeamber-fox-0421). - List runs:
loopmux runs ls
- Output includes each run version and whether it matches local version.
- Send controls to a run by id or name:
loopmux runs hold <id-or-name> loopmux runs resume <id-or-name> loopmux runs next <id-or-name> loopmux runs renew <id-or-name> loopmux runs stop <id-or-name>
- Open the fleet manager TUI:
loopmux runs tui
- On wide terminals, fleet manager uses a split layout (runs list on left, selected-run details on right).
- Controls:
</Leftprevious,>/Rightnext,xtoggle stale visibility (hidden by default),vmismatch-only filter,fcycle state filter (all/active/holding/stale),/search mode (name/id/target/state/version),sarm stop for selected run,Enterconfirm armed stop (or jump when no stop is armed),ccancel armed stop,icopy selected run id,ycopyloopmux runs stop <id>snippet,hhold,rresume,nnext,Rrenew,q/Escquit manager. - When opened from
run --tuiviaf,q/Escreturns to the run view. - Header includes local version plus counts (
active,holding,stale,mismatch).
loopmux --versionshows the active binary version.loopmux run --helpandloopmux runs --helpinclude version references.- Fleet manager and run TUI surfaces include version labels to spot mismatched runs quickly.
-t, --target: tmux scope selector.- omit
-t: scan all sessions/windows/panes each poll session: all panes in that sessionsession:window: all panes in that windowsession:window.pane: one pane
- omit
-n, --iterations: number of iterations (omit for infinite when using config).
-t 0expands tocurrent_session:current_window.0-t 2.1expands tocurrent_session:2.1- Shorthand requires tmux; otherwise provide full
session:window.pane.
- Shorthand requires tmux; otherwise provide full
When no candidates are found in the selected scope, loopmux waits and re-scans on the next --poll interval.
By default, loopmux sends only on trigger state transitions (false -> true) per target/rule and waits for the trigger to clear before sending again.
By default, loopmux also requires matches to remain present for 5s before sending (trigger_confirm_seconds). Set it to 0 for immediate behavior, or override per rule with confirm_seconds.
- Verify the target:
tmux list-panes -a -F '#{session_name}:#{window_index}.#{pane_index}' - Ensure the session/window/pane exists and is attached.
- Confirm the assistant is running in the target pane.
- Check your match regex/contains.
- Confirm the pane output actually includes the trigger text.
- Use
multi_matchif you expect more than one rule to fire.
- Adjust
delay(fixed/range/jitter/backoff). - Increase
min/maxif output needs more time to settle.
- If you see
error: unable to run rust-objcopy, ensure you have the latest tap:brew update brew reinstall loopmux
- The formula disables cargo stripping to avoid this dependency on macOS.
- If install fails with
Formula reports different checksum, your local tap formula is stale or the formula SHA is outdated. - Refresh your taps and retry:
brew update brew untap dmoliveira/tap brew tap dmoliveira/tap brew reinstall loopmux
- Maintainers can regenerate
release/loopmux.rbfor a release tag:./release/update_formula.sh v0.1.6
- Fork the repo and create a feature branch.
- Run
cargo fmtandcargo checkbefore opening a PR. - Keep commits focused and include a clear summary.
loopmux executes prompts into tmux. Treat configs as code, review commands, and avoid sensitive content in logs.
MIT