feat: add tmux-bridge script for cross-pane agent communication - #1329
Conversation
From https://github.com/ShawnPana/smux - enables AI agents to read, type, and send keys to other tmux panes via a bash CLI. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
You do not have enough credits to review this pull request. Please purchase more credits to continue. |
Mesa DescriptionTL;DRAdded the What changed?
Description generated by Mesa. Update settings |
There was a problem hiding this comment.
2 issues found across 2 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="home-manager/modules/local-scripts/tmux-bridge.sh">
<violation number="1" location="home-manager/modules/local-scripts/tmux-bridge.sh:227">
P3: `cmd_type` only sends the second argument, so unquoted multi-word text is truncated (e.g., `hello world` becomes `hello`). Consider shifting the target and sending the remaining args as a single message.</violation>
<violation number="2" location="home-manager/modules/local-scripts/tmux-bridge.sh:251">
P3: `cmd_message` only includes the second argument, so unquoted multi-word messages are truncated. Consider shifting the target and appending all remaining args to the header.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| session_win=$(tmx display-message -t "$sender_pane" -p '#{session_name}:#{window_index}.#{pane_index}' 2>/dev/null || true) | ||
|
|
||
| local header="[tmux-bridge from:${from} pane:${sender_pane} at:${session_win} — load the smux skill to reply]" | ||
| tmx send-keys -t "$target" -l -- "${header} $2" |
There was a problem hiding this comment.
P3: cmd_message only includes the second argument, so unquoted multi-word messages are truncated. Consider shifting the target and appending all remaining args to the header.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At home-manager/modules/local-scripts/tmux-bridge.sh, line 251:
<comment>`cmd_message` only includes the second argument, so unquoted multi-word messages are truncated. Consider shifting the target and appending all remaining args to the header.</comment>
<file context>
@@ -0,0 +1,404 @@
+ session_win=$(tmx display-message -t "$sender_pane" -p '#{session_name}:#{window_index}.#{pane_index}' 2>/dev/null || true)
+
+ local header="[tmux-bridge from:${from} pane:${sender_pane} at:${session_win} — load the smux skill to reply]"
+ tmx send-keys -t "$target" -l -- "${header} $2"
+ clear_read "$target"
+}
</file context>
| validate_target "$target" | ||
| require_read "$target" | ||
|
|
||
| tmx send-keys -t "$target" -l -- "$2" |
There was a problem hiding this comment.
P3: cmd_type only sends the second argument, so unquoted multi-word text is truncated (e.g., hello world becomes hello). Consider shifting the target and sending the remaining args as a single message.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At home-manager/modules/local-scripts/tmux-bridge.sh, line 227:
<comment>`cmd_type` only sends the second argument, so unquoted multi-word text is truncated (e.g., `hello world` becomes `hello`). Consider shifting the target and sending the remaining args as a single message.</comment>
<file context>
@@ -0,0 +1,404 @@
+ validate_target "$target"
+ require_read "$target"
+
+ tmx send-keys -t "$target" -l -- "$2"
+ clear_read "$target"
+}
</file context>
There was a problem hiding this comment.
Code Review
This pull request introduces tmux-bridge, a bash-based CLI tool for cross-pane communication within tmux, and integrates it into the home-manager configuration. The script includes features for socket detection, pane labeling, and a 'read-before-act' guard mechanism. Feedback was provided regarding a security vulnerability in the read_guard_path function, where predictable filenames in the world-writable /tmp directory could lead to symlink attacks or denial of service; a more secure approach using user-specific directories was suggested.
| read_guard_path() { | ||
| local pane_id="$1" | ||
| # Sanitize: %66 → _66 | ||
| echo "/tmp/tmux-bridge-read-${pane_id//%/_}" | ||
| } |
There was a problem hiding this comment.
Using predictable filenames in the world-writable /tmp directory can lead to security vulnerabilities (e.g., symlink attacks or denial of service). Another process could create these files before the script does, potentially bypassing the read guard.
It's more secure to use a user-specific directory. A good practice is to use $XDG_RUNTIME_DIR if it's set, falling back to a user-specific directory inside /tmp.
| read_guard_path() { | |
| local pane_id="$1" | |
| # Sanitize: %66 → _66 | |
| echo "/tmp/tmux-bridge-read-${pane_id//%/_}" | |
| } | |
| read_guard_path() { | |
| local pane_id="$1" | |
| local tmpdir="${XDG_RUNTIME_DIR:-/tmp/tmux-bridge-$(id -u)}" | |
| mkdir -p "$tmpdir" | |
| # Sanitize: %66 → _66 | |
| echo "${tmpdir}/read-${pane_id//%/_}" | |
| } |
There was a problem hiding this comment.
Pull request overview
Adds a new tmux-bridge Bash CLI to the Home Manager “local-scripts” module to enable cross-pane communication (list/read/type/keys/label/doctor) within tmux sessions, intended for AI-agent workflows.
Changes:
- Introduce
home-manager/modules/local-scripts/tmux-bridge.shimplementing thetmux-bridgeCLI (socket detection, pane targeting, read-before-act guard, and diagnostic tooling). - Deploy the script to
~/.local/scripts/tmux-bridgeviahome-manager/modules/local-scripts/default.nix.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
| home-manager/modules/local-scripts/tmux-bridge.sh | New tmux-bridge CLI implementation for tmux cross-pane communication. |
| home-manager/modules/local-scripts/default.nix | Installs tmux-bridge into ~/.local/scripts and marks it executable. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| target=$(resolve_target "$1") | ||
| validate_target "$target" | ||
| require_read "$target" | ||
|
|
||
| tmx send-keys -t "$target" -l -- "$2" |
There was a problem hiding this comment.
type only sends $2, so tmux-bridge type <target> hello world will drop everything after the first word unless the caller remembers to quote. Consider consuming the rest of the arguments as the text payload (e.g., join/"$*" after shifting the target) so multi-word input works reliably for agents and humans.
| session_win=$(tmx display-message -t "$sender_pane" -p '#{session_name}:#{window_index}.#{pane_index}' 2>/dev/null || true) | ||
|
|
||
| local header="[tmux-bridge from:${from} pane:${sender_pane} at:${session_win} — load the smux skill to reply]" | ||
| tmx send-keys -t "$target" -l -- "${header} $2" |
There was a problem hiding this comment.
message only appends $2 after the header, which truncates unquoted multi-word messages. Consider treating all remaining args after <target> as the message body so callers don't have to rely on correct quoting.
| tmx send-keys -t "$target" -l -- "${header} $2" | |
| local message_body="${*:2}" | |
| tmx send-keys -t "$target" -l -- "${header} ${message_body}" |
| result=$(tmx list-panes -a -F '#{pane_id} #{@name}' 2>/dev/null \ | ||
| | awk -v lbl="$label" '$2 == lbl { print $1; exit }') |
There was a problem hiding this comment.
Label resolution splits on whitespace (-F '#{pane_id} #{@name}' then awk compares $2), so labels containing spaces (or other whitespace) cannot be resolved correctly. Either enforce/validate that @name contains no whitespace in cmd_name, or change the format to use a delimiter (e.g., |) and parse robustly.
| result=$(tmx list-panes -a -F '#{pane_id} #{@name}' 2>/dev/null \ | |
| | awk -v lbl="$label" '$2 == lbl { print $1; exit }') | |
| result=$(tmx list-panes -a -F '#{pane_id}|#{@name}' 2>/dev/null \ | |
| | awk -F '|' -v lbl="$label" '$2 == lbl { print $1; exit }') |
| read_guard_path() { | ||
| local pane_id="$1" | ||
| # Sanitize: %66 → _66 | ||
| echo "/tmp/tmux-bridge-read-${pane_id//%/_}" |
There was a problem hiding this comment.
The read-guard marker is created in /tmp with a predictable name. On multi-user systems another local user could pre-create that file to bypass the read-before-act guard (sticky bit allows creating new files in /tmp). Consider placing guard files under a per-user 0700 directory (e.g., ${XDG_RUNTIME_DIR:-/tmp}/tmux-bridge-$UID/) to prevent spoofing.
| read_guard_path() { | |
| local pane_id="$1" | |
| # Sanitize: %66 → _66 | |
| echo "/tmp/tmux-bridge-read-${pane_id//%/_}" | |
| read_guard_base_dir() { | |
| # Use per-user runtime dir to avoid predictable files in shared /tmp. | |
| local runtime_dir="${XDG_RUNTIME_DIR:-/tmp}" | |
| local uid | |
| uid=$(id -u) | |
| echo "${runtime_dir}/tmux-bridge-${uid}" | |
| } | |
| ensure_read_guard_dir() { | |
| local dir | |
| dir=$(read_guard_base_dir) | |
| if [[ ! -d "$dir" ]]; then | |
| mkdir -p "$dir" || die "failed to create read guard directory: $dir" | |
| chmod 700 "$dir" || die "failed to set permissions on read guard directory: $dir" | |
| fi | |
| } | |
| read_guard_path() { | |
| local pane_id="$1" | |
| # Sanitize: %66 → _66 | |
| ensure_read_guard_dir | |
| local dir | |
| dir=$(read_guard_base_dir) | |
| echo "${dir}/tmux-bridge-read-${pane_id//%/_}" |
| # Get the actual running process (deepest child of pane pid) | ||
| local proc child_pid | ||
| proc=$(ps -o comm= -p "$pid" 2>/dev/null || echo "?") | ||
| # Find child process (e.g. claude/node running inside zsh) — works on macOS + Linux |
There was a problem hiding this comment.
The comment claims this finds the "deepest child" process, but pgrep -P "$pid" | head -1 only returns an arbitrary direct child. Either adjust the comment to match the behavior, or implement a real descendant walk if you need the innermost process.
| # Get the actual running process (deepest child of pane pid) | |
| local proc child_pid | |
| proc=$(ps -o comm= -p "$pid" 2>/dev/null || echo "?") | |
| # Find child process (e.g. claude/node running inside zsh) — works on macOS + Linux | |
| # Get the displayed process (pane pid, or one direct child if present) | |
| local proc child_pid | |
| proc=$(ps -o comm= -p "$pid" 2>/dev/null || echo "?") | |
| # Prefer a direct child process (e.g. claude/node running inside zsh) — works on macOS + Linux |
📝 WalkthroughWalkthroughA new tmux bridge script is added as an executable to Changes
Sequence DiagramsequenceDiagram
actor User
participant Script as tmux-bridge.sh
participant TmuxServer as Tmux Server
participant GuardFiles as /tmp Guard Files
participant TargetPane as Target Pane
User->>Script: Execute command (e.g., "message")
Script->>Script: Discover socket (TMUX_BRIDGE_SOCKET/<br/>TMUX env/<br/>scan /tmp dirs)
Script->>TmuxServer: Validate socket connectivity
Script->>Script: Resolve target (pane id/<br/>@label/selector)
Script->>TmuxServer: Validate target with display-message
alt Command requires read guard
Script->>GuardFiles: Check if read guard exists
alt Guard missing
Script->>Script: Abort (die)
end
end
Script->>GuardFiles: Write guard file (type/message/keys)
Script->>TmuxServer: Send command to target pane
TargetPane->>TargetPane: Receive input/keys
Script->>GuardFiles: Clear guard file
Script->>User: Return status
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@home-manager/modules/local-scripts/tmux-bridge.sh`:
- Around line 219-229: The cmd_type (and the similar message function) currently
only sends "$2", dropping all remaining argv words; update cmd_type to shift the
first arg (target) and send the rest as one string using the positional slice
expansion (e.g., use "${*:2}" or equivalent) so tmx send-keys receives the full
payload, and apply the same change to the message function (functions: cmd_type
and message) to preserve multi-word input.
- Around line 144-176: resolve_label and resolve_target currently split tmux
output on whitespace and treat the first match as authoritative while cmd_name
allows arbitrary/duplicate labels, so labels with spaces, dots or duplicates can
misroute; fix by enforcing a safe label policy at write time in cmd_name (reject
or normalize labels to a restricted charset and ensure uniqueness) OR make
lookup delimiter-safe and unambiguous in resolve_label/resolve_target by using a
null-delimited/listing mode (or tmux format fields with an exact-name
comparator) and explicitly detect and die on multiple matches; update
resolve_label to perform exact equality checks (not whitespace splitting),
return an error on ambiguous matches, and ensure resolve_target treats reserved
tmux syntaxes consistently with the chosen label policy.
- Around line 305-364: The doctor routine (cmd_doctor) is running after global
init_socket and still uses tmux in places, so stale TMUX/missing-server cases
abort early and pane counts query the wrong server; move the doctor dispatch to
run before init_socket is called and inside cmd_doctor use detect_socket to set
a local TMUX_SOCKET (or export TMUX_SOCKET locally) from detect_socket's result,
then use tmx everywhere in cmd_doctor (replace tmux calls like in the pane/count
logic) so it queries the detected socket and will not trigger set -e aborts;
update any global dispatcher that calls init_socket to call cmd_doctor before
init_socket so doctor can diagnose stale envs.
- Around line 16-35: The current read-guard functions (read_guard_path,
mark_read, require_read, clear_read) key the guard only by pane id ("%N"), which
is not unique across tmux servers or restarts; update read_guard_path to
incorporate the tmux server socket name and a stable pane identifier (e.g.,
pane_pid) into the filename so the guard ties to the specific server+pane
instance; change mark_read to capture and persist the pane's stable identity
when creating the guard file, make require_read validate both the socket and the
persisted pane identity inside the guard, and ensure clear_read removes the
correct server-scoped guard; use tmux queries (socket name and pane_pid via tmux
display/list-panes) to obtain the identifiers in the functions referenced above.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 6a436d49-b53b-452a-b520-a88d0b253bc7
📒 Files selected for processing (2)
home-manager/modules/local-scripts/default.nixhome-manager/modules/local-scripts/tmux-bridge.sh
| read_guard_path() { | ||
| local pane_id="$1" | ||
| # Sanitize: %66 → _66 | ||
| echo "/tmp/tmux-bridge-read-${pane_id//%/_}" | ||
| } | ||
|
|
||
| mark_read() { | ||
| touch "$(read_guard_path "$1")" | ||
| } | ||
|
|
||
| require_read() { | ||
| local guard | ||
| guard=$(read_guard_path "$1") | ||
| if [[ ! -f "$guard" ]]; then | ||
| die "must read the pane before interacting. Run: tmux-bridge read $1" | ||
| fi | ||
| } | ||
|
|
||
| clear_read() { | ||
| rm -f "$(read_guard_path "$1")" |
There was a problem hiding this comment.
Key the read guard by tmux server and pane instance, not just %N.
/tmp/tmux-bridge-read-_1 is reusable across tmux servers and after server restarts, so a previous read %1 can authorize type %1 against a different pane. That breaks the core read-before-act guarantee. Include the detected socket in the guard key and persist a stable pane identity (for example pane_pid) when marking/validating the read.
Proposed fix
read_guard_path() {
local pane_id="$1"
- # Sanitize: %66 → _66
- echo "/tmp/tmux-bridge-read-${pane_id//%/_}"
+ local runtime_dir="${XDG_RUNTIME_DIR:-/tmp}"
+ local socket_key="${TMUX_SOCKET:-__default__}"
+ socket_key="${socket_key//[^[:alnum:]]/_}"
+ echo "${runtime_dir}/tmux-bridge-read-${socket_key}-${pane_id//%/_}"
}
mark_read() {
- touch "$(read_guard_path "$1")"
+ tmx display-message -t "$1" -p '#{pane_pid}' >"$(read_guard_path "$1")"
}
require_read() {
local guard
guard=$(read_guard_path "$1")
if [[ ! -f "$guard" ]]; then
die "must read the pane before interacting. Run: tmux-bridge read $1"
fi
+ local current_pid
+ current_pid=$(tmx display-message -t "$1" -p '#{pane_pid}')
+ [[ "$(cat "$guard")" == "$current_pid" ]] \
+ || die "pane changed since last read. Run: tmux-bridge read $1"
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@home-manager/modules/local-scripts/tmux-bridge.sh` around lines 16 - 35, The
current read-guard functions (read_guard_path, mark_read, require_read,
clear_read) key the guard only by pane id ("%N"), which is not unique across
tmux servers or restarts; update read_guard_path to incorporate the tmux server
socket name and a stable pane identifier (e.g., pane_pid) into the filename so
the guard ties to the specific server+pane instance; change mark_read to capture
and persist the pane's stable identity when creating the guard file, make
require_read validate both the socket and the persisted pane identity inside the
guard, and ensure clear_read removes the correct server-scoped guard; use tmux
queries (socket name and pane_pid via tmux display/list-panes) to obtain the
identifiers in the functions referenced above.
| # Resolve a target: if it looks like a tmux target (%N, session:win.pane, pure digits), | ||
| # use it directly; otherwise treat it as a @name label and resolve. | ||
| resolve_target() { | ||
| local target="$1" | ||
|
|
||
| # tmux pane ID like %0, %12 | ||
| if [[ "$target" =~ ^%[0-9]+$ ]]; then | ||
| echo "$target"; return | ||
| fi | ||
|
|
||
| # Looks like a tmux target with colon or dot (session:win.pane) | ||
| if [[ "$target" == *:* ]] || [[ "$target" == *.* ]]; then | ||
| echo "$target"; return | ||
| fi | ||
|
|
||
| # Pure numeric — treat as window index | ||
| if [[ "$target" =~ ^[0-9]+$ ]]; then | ||
| echo "$target"; return | ||
| fi | ||
|
|
||
| # Otherwise resolve as a @name label | ||
| resolve_label "$target" | ||
| } | ||
|
|
||
| resolve_label() { | ||
| local label="$1" | ||
| local result | ||
| result=$(tmx list-panes -a -F '#{pane_id} #{@name}' 2>/dev/null \ | ||
| | awk -v lbl="$label" '$2 == lbl { print $1; exit }') | ||
| if [[ -z "$result" ]]; then | ||
| die "no pane found with label '$label'" | ||
| fi | ||
| echo "$result" |
There was a problem hiding this comment.
Label-based routing is lossy and can misroute commands.
cmd_name allows arbitrary/duplicate labels, resolve_label splits on whitespace and returns the first match only, and resolve_target reserves dotted/numeric names for tmux syntax. Labels like my bot, api.v1, or duplicate codex values will either fail to resolve or resolve arbitrarily. Please either validate labels on write time (unique + restricted charset) or make lookup delimiter-safe and fail on ambiguity.
Also applies to: 282-289
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@home-manager/modules/local-scripts/tmux-bridge.sh` around lines 144 - 176,
resolve_label and resolve_target currently split tmux output on whitespace and
treat the first match as authoritative while cmd_name allows arbitrary/duplicate
labels, so labels with spaces, dots or duplicates can misroute; fix by enforcing
a safe label policy at write time in cmd_name (reject or normalize labels to a
restricted charset and ensure uniqueness) OR make lookup delimiter-safe and
unambiguous in resolve_label/resolve_target by using a null-delimited/listing
mode (or tmux format fields with an exact-name comparator) and explicitly detect
and die on multiple matches; update resolve_label to perform exact equality
checks (not whitespace splitting), return an error on ambiguous matches, and
ensure resolve_target treats reserved tmux syntaxes consistently with the chosen
label policy.
| cmd_type() { | ||
| require_args 2 $# "type" | ||
| require_tmux | ||
| local target | ||
| target=$(resolve_target "$1") | ||
| validate_target "$target" | ||
| require_read "$target" | ||
|
|
||
| tmx send-keys -t "$target" -l -- "$2" | ||
| clear_read "$target" | ||
| } |
There was a problem hiding this comment.
type and message silently drop everything after the first word.
Both commands only send $2. Any caller that passes the text as multiple argv entries will lose the rest of the payload, which is easy to hit for agent prompts/instructions. Shift the target off first and send the remaining arguments as one string.
Proposed fix
cmd_type() {
require_args 2 $# "type"
require_tmux
local target
target=$(resolve_target "$1")
validate_target "$target"
require_read "$target"
+ shift
- tmx send-keys -t "$target" -l -- "$2"
+ tmx send-keys -t "$target" -l -- "$*"
clear_read "$target"
}
cmd_message() {
require_args 2 $# "message"
@@
local target
target=$(resolve_target "$1")
validate_target "$target"
require_read "$target"
+ shift
# Detect sender identity and location
local sender_pane="${TMUX_PANE:-}"
@@
local header="[tmux-bridge from:${from} pane:${sender_pane} at:${session_win} — load the smux skill to reply]"
- tmx send-keys -t "$target" -l -- "${header} $2"
+ tmx send-keys -t "$target" -l -- "${header} $*"
clear_read "$target"
}Also applies to: 231-253
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@home-manager/modules/local-scripts/tmux-bridge.sh` around lines 219 - 229,
The cmd_type (and the similar message function) currently only sends "$2",
dropping all remaining argv words; update cmd_type to shift the first arg
(target) and send the rest as one string using the positional slice expansion
(e.g., use "${*:2}" or equivalent) so tmx send-keys receives the full payload,
and apply the same change to the message function (functions: cmd_type and
message) to preserve multi-word input.
| cmd_doctor() { | ||
| require_tmux | ||
| local ok=true | ||
|
|
||
| echo "tmux-bridge doctor v${VERSION}" | ||
| echo "---" | ||
|
|
||
| # Environment | ||
| echo "TMUX_PANE: ${TMUX_PANE:-<unset>}" | ||
| echo "TMUX: ${TMUX:-<unset>}" | ||
| echo "TMUX_BRIDGE_SOCKET: ${TMUX_BRIDGE_SOCKET:-<unset>}" | ||
|
|
||
| # Socket from $TMUX | ||
| if [[ -n "${TMUX:-}" ]]; then | ||
| local env_socket="${TMUX%%,*}" | ||
| if [[ -S "$env_socket" ]]; then | ||
| if tmux -S "$env_socket" list-sessions &>/dev/null; then | ||
| echo "\$TMUX socket: $env_socket (reachable)" | ||
| else | ||
| echo "\$TMUX socket: $env_socket (exists but not responding)" | ||
| ok=false | ||
| fi | ||
| else | ||
| echo "\$TMUX socket: $env_socket (MISSING — stale env)" | ||
| ok=false | ||
| fi | ||
| fi | ||
|
|
||
| # Detected socket | ||
| echo "---" | ||
| local detected | ||
| detected=$(detect_socket 2>/dev/null || echo "__failed__") | ||
| if [[ "$detected" == "__failed__" ]]; then | ||
| echo "Detected socket: FAILED — no reachable tmux server found" | ||
| ok=false | ||
| elif [[ "$detected" == "__default__" ]]; then | ||
| echo "Detected socket: (default tmux server)" | ||
| else | ||
| echo "Detected socket: $detected" | ||
| fi | ||
|
|
||
| # Pane visibility | ||
| echo "---" | ||
| if [[ -n "${TMUX_PANE:-}" && "$detected" != "__failed__" ]]; then | ||
| if tmx display-message -t "$TMUX_PANE" -p '#{pane_id}' &>/dev/null; then | ||
| echo "This pane ($TMUX_PANE): visible to server" | ||
| else | ||
| echo "This pane ($TMUX_PANE): NOT visible to server" | ||
| ok=false | ||
| fi | ||
| fi | ||
|
|
||
| # Pane count | ||
| if [[ "$detected" != "__failed__" ]]; then | ||
| local count | ||
| count=$(tmx list-panes -a -F '#{pane_id}' 2>/dev/null | wc -l | tr -d ' ') | ||
| echo "Total panes: $count" | ||
| local labeled | ||
| labeled=$(tmx list-panes -a -F '#{@name}' 2>/dev/null | grep -cv '^$' || echo 0) | ||
| echo "Labeled panes: $labeled" |
There was a problem hiding this comment.
doctor bails out before the failure mode it is supposed to diagnose.
The main dispatcher still runs init_socket before cmd_doctor, so stale TMUX/missing-server cases exit before any diagnostics print. Inside cmd_doctor, the pane counts also use raw tmux instead of tmx, so a non-default socket is reported against the wrong server and can abort under set -e. Let doctor run before global socket init and bind TMUX_SOCKET from the locally detected value inside the command.
Proposed fix
case "$1" in
id) shift; cmd_id "$@"; exit ;;
+ doctor) shift; cmd_doctor "$@"; exit ;;
-h|--help|help) usage ;;
version) echo "tmux-bridge $VERSION"; exit ;;
esac
@@
detected=$(detect_socket 2>/dev/null || echo "__failed__")
if [[ "$detected" == "__failed__" ]]; then
echo "Detected socket: FAILED — no reachable tmux server found"
ok=false
elif [[ "$detected" == "__default__" ]]; then
echo "Detected socket: (default tmux server)"
+ TMUX_SOCKET="__default__"
else
echo "Detected socket: $detected"
+ TMUX_SOCKET="$detected"
fi
@@
if [[ "$detected" != "__failed__" ]]; then
local count
- count=$(tmux list-panes -a -F '#{pane_id}' 2>/dev/null | wc -l | tr -d ' ')
+ count=$(tmx list-panes -a -F '#{pane_id}' 2>/dev/null | wc -l | tr -d ' ')
echo "Total panes: $count"
local labeled
- labeled=$(tmux list-panes -a -F '#{`@name`}' 2>/dev/null | grep -cv '^$' || echo 0)
+ labeled=$(tmx list-panes -a -F '#{`@name`}' 2>/dev/null | grep -cv '^$' || echo 0)
echo "Labeled panes: $labeled"
fiAlso applies to: 383-402
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@home-manager/modules/local-scripts/tmux-bridge.sh` around lines 305 - 364,
The doctor routine (cmd_doctor) is running after global init_socket and still
uses tmux in places, so stale TMUX/missing-server cases abort early and pane
counts query the wrong server; move the doctor dispatch to run before
init_socket is called and inside cmd_doctor use detect_socket to set a local
TMUX_SOCKET (or export TMUX_SOCKET locally) from detect_socket's result, then
use tmx everywhere in cmd_doctor (replace tmux calls like in the pane/count
logic) so it queries the detected socket and will not trigger set -e aborts;
update any global dispatcher that calls init_socket to call cmd_doctor before
init_socket so doctor can diagnose stale envs.
Summary
tmux-bridgeCLI from ShawnPana/smux to local-scripts~/.local/scripts/tmux-bridgevia home-managerTest plan
home-manager switchand verifytmux-bridgeis available in PATHtmux-bridge listinside a tmux sessiontmux-bridge doctorto verify connectivity🤖 Generated with Claude Code
Summary by cubic
Add
tmux-bridgeCLI to~/.local/scriptsviahome-managerfor cross-pane communication in tmux. Tools can read panes, type text, send keys, and use label-based targeting with a read-before-act guard.New Features
tmux-bridgeand exposes commands: list, read, type, keys, message, name, resolve, id, doctor.@name), sender-awaremessageheaders, and auto tmux socket detection with adoctordiagnostic.Migration
home-manager switch; confirmtmux-bridgeis in PATH.tmux-bridge list, thentmux-bridge read <target>beforetype,keys, ormessage.TMUX_BRIDGE_SOCKETif the default tmux socket cannot be detected.Written for commit 2d13624. Summary will update on new commits.