Skip to content

dmoliveira/loopmux

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

124 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

loopmux

Homebrew

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).

Why loopmux

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.

Features

  • YAML config with pre, prompt, post blocks
  • Ordered rule evaluation with first_match, priority, or multi_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)

Supported Code Assistants

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

Install

Homebrew

brew tap dmoliveira/tap
brew install loopmux

Build from source

git clone https://github.com/dmoliveira/loopmux.git
cd loopmux
cargo build --release
./target/release/loopmux --help

Quick Start

  1. Create a config:
loopmux init --output loop.yaml
  1. Update the tmux target and rules in loop.yaml.

  2. Validate config:

loopmux validate --config loop.yaml
  1. Run the loop:
loopmux run --config loop.yaml

Quick Run (no YAML)

loopmux run -t ai:5.0 -n 5 \
  --prompt "Do the next iteration." \
  --trigger "Concluded|What is next" \
  --once

Configuration

Minimal example

target: "ai:5.0"
iterations: 10

default_action:
  prompt: "Do the next iteration."

Full example

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"

Example files

  • examples/loopmux.example.yaml
  • examples/loopmux.lean.yaml

Rule evaluation

  • first_match: ordered rules; first match wins.
  • multi_match: all matching rules fire in order.
  • priority: highest priority wins (ties resolved by order).

Delay strategies

  • fixed: static delay in seconds.
  • range: random delay between min and max.
  • jitter: range plus +/- jitter factor (0.0..1.0).
  • backoff: exponential backoff using base, factor, max.

CLI

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>

Lean Mode (no YAML)

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

Lean flags

  • --prompt: required prompt body.
  • --trigger: regex to match tmux output (required).
  • --trigger-exact-line: treat --trigger as 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.

TUI history picker

  • Run loopmux run --tui with no prompt/config to pick from recent commands.
  • Entries are stored in ~/.loopmux/history.json, newest first, deduplicated by command shape.
  • TUI controls: h hold/resume (non-consuming, alias p/r), f open fleet manager view, R renew counter, n next, s/Ctrl+C stop run, q quit run view.
  • When --duration is 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 (N lines from capture tail) to keep long prompts readable.
  • TUI log timestamps use subtle date-aware coloring to make same-day activity easier to scan.

Fleet manager (local)

  • Every running loopmux run writes a local registry entry under ~/.loopmux/runs/state/.
  • Each run has an id plus a codename (--name or auto-generated like amber-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: </Left previous, >/Right next, x toggle stale visibility (hidden by default), v mismatch-only filter, f cycle state filter (all/active/holding/stale), / search mode (name/id/target/state/version), s arm stop for selected run, Enter confirm armed stop (or jump when no stop is armed), c cancel armed stop, i copy selected run id, y copy loopmux runs stop <id> snippet, h hold, r resume, n next, R renew, q/Esc quit manager.
    • When opened from run --tui via f, q/Esc returns to the run view.
    • Header includes local version plus counts (active, holding, stale, mismatch).

Version checks

  • loopmux --version shows the active binary version.
  • loopmux run --help and loopmux runs --help include version references.
  • Fleet manager and run TUI surfaces include version labels to spot mismatched runs quickly.

Common flags

  • -t, --target: tmux scope selector.
    • omit -t: scan all sessions/windows/panes each poll
    • session: all panes in that session
    • session:window: all panes in that window
    • session:window.pane: one pane
  • -n, --iterations: number of iterations (omit for infinite when using config).

Target shorthand (inside tmux)

  • -t 0 expands to current_session:current_window.0
  • -t 2.1 expands to current_session:2.1
    • Shorthand requires tmux; otherwise provide full session:window.pane.

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.

Troubleshooting

tmux target not found

  • 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.

No triggers firing

  • Check your match regex/contains.
  • Confirm the pane output actually includes the trigger text.
  • Use multi_match if you expect more than one rule to fire.

Too fast or too slow

  • Adjust delay (fixed/range/jitter/backoff).
  • Increase min/max if output needs more time to settle.

Homebrew build fails with rust-objcopy

  • 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.

Homebrew checksum mismatch

  • 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.rb for a release tag:
    ./release/update_formula.sh v0.1.6

Contributing

  1. Fork the repo and create a feature branch.
  2. Run cargo fmt and cargo check before opening a PR.
  3. Keep commits focused and include a clear summary.

Security

loopmux executes prompts into tmux. Treat configs as code, review commands, and avoid sensitive content in logs.

License

MIT

About

A ralph-loop support for Tmux

Resources

Stars

Watchers

Forks

Packages

No packages published

Languages