#!/bin/bash # hermes-dispatch — watch the queue, feed tasks into the hermes tmux pane # Fire-and-forget: dispatches next task as soon as the prompt is visible. # Does NOT wait for completion — just ensures hermes is idle before sending. set -euo pipefail QUEUE_DIR="$HOME/.hermes/queue" TARGET_PANE="${HERMES_PANE:-Hermes:0.0}" POLL_INTERVAL="${POLL_INTERVAL:-2}" log() { echo "[$(date +%H:%M:%S)] $*"; } is_hermes_idle() { # Strip blank lines, check bottom of pane for the input prompt local content content=$(tmux capture-pane -t "$TARGET_PANE" -p 2>/dev/null | grep -v '^$' | tail -4) echo "$content" | grep -qE '⚕ ❯|type a message' } pick_next_task() { for pri in high normal low; do local task task=$(ls "$QUEUE_DIR/pending/${pri}_"*.task 2>/dev/null | head -1) if [[ -n "$task" ]]; then echo "$task" return fi done } dispatch_task() { local task_file="$1" local basename=$(basename "$task_file") local prompt prompt=$(grep '^PROMPT=' "$task_file" | sed 's/^PROMPT=//') if [[ -z "$prompt" ]]; then log "ERROR: empty prompt in $basename" mv "$task_file" "$QUEUE_DIR/failed/$basename" return fi log "DISPATCH: $basename" log " → ${prompt:0:100}" # Move to done immediately (fire-and-forget) mv "$task_file" "$QUEUE_DIR/done/$basename" # Send to pane tmux send-keys -t "$TARGET_PANE" "$prompt" Enter } # --- Main loop --- log "hermes-dispatch started (fire-and-forget mode)" log "Pane: $TARGET_PANE | Poll: ${POLL_INTERVAL}s" log "Watching: $QUEUE_DIR/pending/" while true; do task=$(pick_next_task) if [[ -n "$task" ]]; then if is_hermes_idle; then dispatch_task "$task" # Give hermes a moment to start processing before next poll sleep 8 fi fi sleep "$POLL_INTERVAL" done