feat(@scripts): ✨ add send command for tmux session prompts
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
parent
80b421a21f
commit
4d1175687b
1 changed files with 104 additions and 0 deletions
104
bin/rclaude
104
bin/rclaude
|
|
@ -320,6 +320,41 @@ claude_slug() {
|
|||
printf %s "$1" | sed 's|[^A-Za-z0-9]|-|g'
|
||||
}
|
||||
|
||||
# POSIX single-quote escape — safe for embedding arbitrary user-provided
|
||||
# strings inside an outer single-quoted context (e.g. ssh '<remote-cmd>').
|
||||
# sh_quote "" → ''
|
||||
# sh_quote "hello" → 'hello'
|
||||
# sh_quote "it's" → 'it'\''s'
|
||||
# The classic '\'' trick: close the open quote, escape a literal ', reopen.
|
||||
sh_quote() {
|
||||
printf "'%s'" "$(printf %s "$1" | sed "s/'/'\\\\''/g")"
|
||||
}
|
||||
|
||||
# Row filter for the send subcommand. Reads TSV rows on stdin
|
||||
# (host \t kind \t name/slug \t detail \t [cwd]) and emits the subset
|
||||
# matching <selector> with <pattern>. Disk rows are always dropped — you
|
||||
# can't `tmux send-keys` to a session that isn't running.
|
||||
#
|
||||
# selector = all → every tmux row (pattern ignored)
|
||||
# selector = host → tmux rows where col 1 == pattern
|
||||
# selector = match → tmux rows where pattern (case-insensitive
|
||||
# substring) appears in col 3 (session
|
||||
# name slug) OR col 5 (cwd, if present)
|
||||
filter_targets() {
|
||||
_selector=$1
|
||||
_pattern=$2
|
||||
awk -F'\t' -v sel="$_selector" -v pat="$_pattern" '
|
||||
BEGIN { IGNORECASE = 1 }
|
||||
$2 != "tmux" { next }
|
||||
sel == "all" { print; next }
|
||||
sel == "host" { if ($1 == pat) print; next }
|
||||
sel == "match" {
|
||||
if (index(tolower($3), tolower(pat)) > 0) { print; next }
|
||||
if (NF >= 5 && index(tolower($5), tolower(pat)) > 0) { print; next }
|
||||
}
|
||||
'
|
||||
}
|
||||
|
||||
# Dedupe session rows (col 2 == "session") across hosts by UUID (col 3),
|
||||
# keeping the row with the highest mtime (col 6). Output sorted desc by mtime.
|
||||
dedupe_sessions() {
|
||||
|
|
@ -1288,6 +1323,74 @@ cmd_version() {
|
|||
fi
|
||||
}
|
||||
|
||||
cmd_send() {
|
||||
# Broadcast a prompt to live rclaude tmux sessions matching a selector.
|
||||
# Useful for batch-prompting agents you have running across hosts.
|
||||
#
|
||||
# rclaude send "fix the build" → all live rclaude tmuxes
|
||||
# rclaude send --on apricot "context update" → only apricot
|
||||
# rclaude send --match lilith "rebase main" → tmux name or cwd matches
|
||||
# rclaude send --no-enter "draft only..." → don't press Enter after
|
||||
# rclaude send --dry-run "..." → show targets, don't send
|
||||
_selector=all
|
||||
_pattern=""
|
||||
_enter=1
|
||||
_dry=0
|
||||
_text=""
|
||||
while [ $# -gt 0 ]; do
|
||||
case $1 in
|
||||
--on) shift; _selector=host; _pattern=${1:-}; shift ;;
|
||||
--on=*) _selector=host; _pattern=${1#--on=}; shift ;;
|
||||
--match) shift; _selector=match; _pattern=${1:-}; shift ;;
|
||||
--match=*) _selector=match; _pattern=${1#--match=}; shift ;;
|
||||
--no-enter) _enter=0; shift ;;
|
||||
--dry-run|-n) _dry=1; shift ;;
|
||||
--) shift; _text=$*; break ;;
|
||||
-h|--help)
|
||||
cat <<EOF
|
||||
usage: rclaude send [--on <host>|--match <pat>] [--no-enter] [--dry-run] <text...>
|
||||
|
||||
Broadcasts <text> to every live rclaude tmux session matching the selector
|
||||
via 'tmux send-keys'. Disk-only sessions are skipped (can't send-keys to
|
||||
something not running).
|
||||
EOF
|
||||
return 2 ;;
|
||||
*)
|
||||
if [ -z "$_text" ]; then _text=$1
|
||||
else _text="$_text $1"
|
||||
fi
|
||||
shift ;;
|
||||
esac
|
||||
done
|
||||
if [ -z "$_text" ]; then
|
||||
echo "rclaude send: missing text. See 'rclaude send --help'" >&2
|
||||
exit 2
|
||||
fi
|
||||
_rows=$(scan_hosts | while IFS= read -r _h; do list_tmux_on "$_h"; done)
|
||||
_targets=$(printf '%s\n' "$_rows" | filter_targets "$_selector" "$_pattern")
|
||||
if [ -z "$_targets" ]; then
|
||||
echo "rclaude send: no matching live tmux sessions" >&2
|
||||
exit 1
|
||||
fi
|
||||
_count=$(printf '%s\n' "$_targets" | wc -l | tr -d ' ')
|
||||
printf 'rclaude send: %s target(s)\n' "$_count" >&2
|
||||
printf '%s\n' "$_targets" | while IFS=$(printf '\t') read -r _h _kind _name _rest; do
|
||||
printf ' → %s : %s\n' "$_h" "$_name" >&2
|
||||
[ "$_dry" = "1" ] && continue
|
||||
_qtext=$(sh_quote "$_text")
|
||||
_qname=$(sh_quote "$_name")
|
||||
if is_local "$_h"; then
|
||||
tmux send-keys -t "$_name" -l "$_text"
|
||||
[ "$_enter" = "1" ] && tmux send-keys -t "$_name" Enter
|
||||
else
|
||||
ssh -o BatchMode=yes -o ConnectTimeout=5 "$_h" \
|
||||
"tmux send-keys -t $_qname -l $_qtext"
|
||||
[ "$_enter" = "1" ] && \
|
||||
ssh -o BatchMode=yes "$_h" "tmux send-keys -t $_qname Enter"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
cmd_voice() {
|
||||
# `rclaude voice` — toggle / inspect the rvoice push-to-talk binding.
|
||||
# rvoice itself is a separate script (bin/rvoice) driven by Hammerspoon.
|
||||
|
|
@ -1362,6 +1465,7 @@ case ${1:-} in
|
|||
send) shift; cmd_send "$@"; exit ;;
|
||||
setup|install) shift; cmd_setup "$@"; exit ;;
|
||||
voice) shift; cmd_voice "$@"; exit ;;
|
||||
send) shift; cmd_send "$@"; exit ;;
|
||||
-v|--version) cmd_version; exit ;;
|
||||
-h|--help|help) cmd_help; exit ;;
|
||||
esac
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue