diff --git a/bin/rclaude b/bin/rclaude index 997516a..013576b 100755 --- a/bin/rclaude +++ b/bin/rclaude @@ -675,21 +675,37 @@ cmd_triage() { printf "%-8s %-8s %-3s %-15s %-50s %s\n" \ "HOST" "UUID" "PRI" "STATUS" "SUMMARY" "NEXT ACTION" fi - scan_hosts | while IFS= read -r h; do - # Row format from list_triage_on: - # host \t triage \t uuid \t priority \t status \t summary \t next_action \t cwd \t mtime + # Parallel fan-out: each list_triage_on does an ssh round-trip + remote + # Haiku batch inference, so a sequential `while read` loop serializes + # minutes of remote LLM work. Run each host concurrently into a per-host + # temp file, wait, then emit in stable scan_hosts order. Hosts are simple + # identifiers (local/apricot/plum) so unquoted word-splitting is safe. + _hosts=$(scan_hosts) + _tdir=$(mktemp -d /tmp/rclaude-triage.XXXXXX 2>/dev/null || echo /tmp/rclaude-triage.$$) + mkdir -p "$_tdir" 2>/dev/null + _n=0 + for h in $_hosts; do + _n=$((_n + 1)) + # shellcheck disable=SC2086 + list_triage_on "$h" $_opts > "$_tdir/$_n" & + done + wait + # Concatenate in scan_hosts order. Row format from list_triage_on: + # host \t triage \t uuid \t priority \t status \t summary \t next_action \t cwd \t mtime + _n=0 + for h in $_hosts; do + _n=$((_n + 1)) if [ "$_tsv" -eq 1 ]; then - # shellcheck disable=SC2086 - list_triage_on "$h" $_opts + cat "$_tdir/$_n" else - # shellcheck disable=SC2086 - list_triage_on "$h" $_opts | awk -F'\t' ' + awk -F'\t' ' { uuid8 = substr($3, 1, 8) printf "%-8s %-8s %-3s %-15s %-50.50s %s\n", $1, uuid8, $4, $5, $6, $7 } - ' + ' "$_tdir/$_n" fi done + rm -rf "$_tdir" } cmd_list() { @@ -941,7 +957,28 @@ cmd_resume() { # but also enforce a per-host floor below — otherwise a host with many # fresh P0/P1 sessions (e.g. local with 2k+ jsonls) takes every seat and # remote hosts disappear from the picker entirely. - _triage_raw=$(scan_hosts | while IFS= read -r h; do list_triage_on "$h"; done) + # Parallel fan-out (see cmd_triage): each list_triage_on is a slow ssh + # + remote Haiku call, so run hosts concurrently into per-host temp + # files and concatenate in stable scan_hosts order. The whole block + # runs inside this `$(...)` subshell, so its `wait` reaps its own + # backgrounded children correctly. + _triage_raw=$( + _t_hosts=$(scan_hosts) + _t_dir=$(mktemp -d /tmp/rclaude-triage.XXXXXX 2>/dev/null || echo /tmp/rclaude-triage.$$) + mkdir -p "$_t_dir" 2>/dev/null + _t_n=0 + for _t_h in $_t_hosts; do + _t_n=$((_t_n + 1)) + list_triage_on "$_t_h" > "$_t_dir/$_t_n" & + done + wait + _t_n=0 + for _t_h in $_t_hosts; do + _t_n=$((_t_n + 1)) + cat "$_t_dir/$_t_n" + done + rm -rf "$_t_dir" + ) _TAB=$(printf '\t') _triage=$(printf '%s\n' "$_triage_raw" | grep -v '^$' \ | sort -t"$_TAB" -k4,4n -k9,9nr) @@ -1474,6 +1511,25 @@ case ${1:-} in -h|--help|help) cmd_help; exit ;; esac +# Mis-placed-flag guard: any first arg that survived the subcommand case AND +# starts with `-` is a flag in host position — the default-dispatch below +# would otherwise treat it as a hostname and emit a confusing "directory not +# found on " error. Catch it here with an actionable message. Bare +# hostnames, `.`, and paths never start with `-`, so they fall through. +case ${1:-} in + --on|--on=*) + echo "rclaude: '$1' is not a valid host or subcommand." >&2 + echo " --on is only valid as: rclaude resume [pattern] --on " >&2 + echo " To start a session on a host, use: rclaude [dir] (e.g. rclaude apricot)" >&2 + exit 2 ;; + -*) + echo "rclaude: '$1' is not a valid host or subcommand." >&2 + echo " usage: rclaude [] []" >&2 + echo " rclaude list|resume|triage|send|kill|setup" >&2 + echo " rclaude resume [pattern] --on " >&2 + exit 2 ;; +esac + # --------------------------------------------------------------------------- # Default behavior: launch (or reattach to) a session. # ---------------------------------------------------------------------------