Compare commits

..

9 Commits

Author SHA1 Message Date
688f263323 Auto-detach when all agents in window exit
Add agent-cmd.sh wrapper that detaches the client when all panes in the
current window have exited. This allows multiple agent windows (one per
directory) while automatically returning to the shell when done with a
particular window's agents.

- remain-on-exit keeps panes alive after agent exits to check status
- Wrapper counts running panes and detaches when it's the last one
2026-02-02 11:49:26 +00:00
c93f8f2633 Prefer opencode to gemini 2026-02-01 19:29:03 +00:00
bb0bc511a6 Add <prefix>A binding to display-popup with agent.sh
Enables the use of AI agents (claude, gemini) in a persistent
display-popup, backed by a tmux session, via the <prefix>A binding.
2026-01-29 11:26:47 +00:00
2d4858ac11 Sort display-popup bindings 2026-01-27 11:07:59 +00:00
2bdc15debd Add display-popup fallback bindings
While display-popup was added in 3.2 the -B, -b, and -S flags were not
introduced until 3.3. This adds fallback bindings which forego these
flags to retain functionality at the cost of aesthetics.
2026-01-27 11:01:59 +00:00
50d9c3be28 Fix version checks 2026-01-23 17:52:19 +00:00
416dc73906 Adjust layout width divisor
More closely match the width/height ration to how the fonts is rendered,
making the ratio of 100 closer to square.
2026-01-02 11:43:35 +00:00
df7a4d581b Implement macOS status-right-length increase 2025-12-04 16:44:50 +00:00
c2ff680dd3 Actually do version comparison for display-popup 2025-12-04 16:44:25 +00:00
7 changed files with 186 additions and 13 deletions

15
agent-cmd.sh Executable file
View File

@@ -0,0 +1,15 @@
#!/usr/bin/env bash
# Wrapper that runs an agent command, then detaches if all panes in window are dead
# Run the agent command (don't use set -e, agent may exit non-zero)
"$@" || true
# Brief delay to let tmux update pane status
sleep 0.1
# Count panes still running (pane_dead=0)
# Note: our own pane counts as running since this script is executing
running=$(tmux list-panes -F '#{pane_dead}' | grep -c '^0$' || true)
# If we're the only pane still running, all others are dead - detach
[ "$running" -le 1 ] && tmux detach-client

57
agent.sh Executable file
View File

@@ -0,0 +1,57 @@
#!/usr/bin/env bash
set -e
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Check which agent commands are available
agents=()
command -v claude &>/dev/null && agents+=(claude)
command -v opencode &>/dev/null && agents+=(opencode)
command -v gemini &>/dev/null && agents+=(gemini)
if [ ${#agents[@]} -eq 0 ]; then
echo "No agent commands found (claude, gemini)"
exit 1
fi
# Use the current directory (set via display-popup -d)
dir="${PWD:-$HOME}"
# Use the directory basename as window name
window_name=$dir
# Create the agent session if it doesn't exist
if ! tmux has-session -t agent; then
tmux new-session -ds agent -c "$dir" -n "$window_name" "$script_dir/agent-cmd.sh" "${agents[0]}"
[ ${#agents[@]} -gt 1 ] && tmux split-window -h -t agent:0 "$script_dir/agent-cmd.sh" "${agents[1]}"
# Store the directory in a window option for later matching
tmux set-window-option -t agent @agent_dir "$dir"
tmux set-option -t agent status off
tmux set-option -t agent remain-on-exit on
tmux attach-session -t agent
exit 0
fi
# Search for an existing window with matching directory
target_window=""
for window_id in $(tmux list-windows -t agent -F '#{window_id}'); do
window_dir=$(tmux show-window-option -t "$window_id" -v @agent_dir 2>/dev/null || true)
if [ "$window_dir" = "$dir" ]; then
target_window="$window_id"
break
fi
done
if [ -n "$target_window" ]; then
# Select the existing window
tmux select-window -t "$target_window"
else
# Create a new window with agents
tmux new-window -t agent -c "$dir" -n "$window_name" "$script_dir/agent-cmd.sh" "${agents[0]}"
[ ${#agents[@]} -gt 1 ] && tmux split-window -h -t agent -c "$dir" "$script_dir/agent-cmd.sh" "${agents[1]}"
# Store the directory in a window option for later matching
tmux set-window-option -t agent @agent_dir "$dir"
fi
# Attach to the session
tmux attach-session -t agent

96
check-version.sh Executable file
View File

@@ -0,0 +1,96 @@
#!/usr/bin/env bash
# Compare the current tmux version against a given version.
# Exit: 0 if comparison is true, 1 if false
set -euo pipefail
usage() {
echo "usage: $0 <operator> <version>" >&2
echo "operators: < <= == >= >" >&2
}
if [[ ${1:-} == "-h" || ${1:-} == "--help" ]]; then
usage
exit 0
fi
if [[ $# -ne 2 ]]; then
usage
exit 1
fi
operator="$1"
target_version="$2"
# Extract tmux version (e.g., "tmux 3.3a" -> "3.3a")
current_version=$(tmux -V | sed 's/^tmux //')
# Compare two version strings
# Returns: -1 if v1 < v2, 0 if v1 == v2, 1 if v1 > v2
compare_versions() {
local v1="$1"
local v2="$2"
# Strip trailing letters (e.g., "3.3a" -> "3.3") and save them
local v1_suffix="${v1##*[0-9]}"
local v2_suffix="${v2##*[0-9]}"
v1="${v1%[a-zA-Z]*}"
v2="${v2%[a-zA-Z]*}"
# Split into components
IFS='.' read -ra v1_parts <<< "$v1"
IFS='.' read -ra v2_parts <<< "$v2"
# Compare numeric parts
local max_len=$(( ${#v1_parts[@]} > ${#v2_parts[@]} ? ${#v1_parts[@]} : ${#v2_parts[@]} ))
for ((i = 0; i < max_len; i++)); do
local p1="${v1_parts[i]:-0}"
local p2="${v2_parts[i]:-0}"
if ((p1 < p2)); then
echo -1
return
elif ((p1 > p2)); then
echo 1
return
fi
done
# Numeric parts equal, compare suffixes (no suffix < a < b < ...)
# A letter suffix indicates a patch release, so 3.6a > 3.6
if [[ -z "$v1_suffix" && -n "$v2_suffix" ]]; then
echo -1
return
elif [[ -n "$v1_suffix" && -z "$v2_suffix" ]]; then
echo 1
return
elif [[ "$v1_suffix" < "$v2_suffix" ]]; then
echo -1
return
elif [[ "$v1_suffix" > "$v2_suffix" ]]; then
echo 1
return
fi
echo 0
}
result=$(compare_versions "$current_version" "$target_version")
case "$operator" in
'<')
[[ "$result" -eq -1 ]] && exit 0 || exit 1 ;;
'<=')
[[ "$result" -le 0 ]] && exit 0 || exit 1 ;;
'==')
[[ "$result" -eq 0 ]] && exit 0 || exit 1 ;;
'>=')
[[ "$result" -ge 0 ]] && exit 0 || exit 1 ;;
'>')
[[ "$result" -eq 1 ]] && exit 0 || exit 1 ;;
*)
echo "error: invalid operator: $operator" >&2
usage
exit 1
;;
esac

View File

@@ -4,6 +4,6 @@ session_name=$(tmux display-message -p '#S')
session_layout=~/.local/share/tmux/layouts/session-$session_name
if [ -f $session_layout ]; then
$session_layout
else
elif [ "$session_name" != "agent" ]; then
tmux rename-window home
fi

View File

@@ -2,7 +2,7 @@
cols=`tmux display -p "#{pane_width}"`
lines=`tmux display -p "#{pane_height}"`
width=$(( $cols / 2 )).0
width=$(( $cols / 2.5 ))
height=$lines.0
ratio=$(( ($width / $height) * 100 ))

View File

@@ -11,7 +11,8 @@ setw -g status-style fg=colour240,bg=colour233
# Right status style shows system info, date, and time.
setw -g status-right "#[fg=colour240]#(cat ~/.cache/tmux/system-info)#[fg=white] %a %d-%m-%y %H:%M "
setw -g status-right-style fg=white,bg=colour233
if -b '[ "`uname`" != "Darwin" ]' \
if -b '[ "`uname`" = "Darwin" ]' \
'run "tmux setw -g status-right-length $((`sysctl -n hw.ncpu` + 48))"' \
'run "tmux setw -g status-right-length $((`nproc --all` + 48))"'
# Active window status style

View File

@@ -28,7 +28,7 @@ set -g status-interval 2
set -g default-terminal "tmux-256color"
# Enable true-color
if '[[ "$TMUX_VERSION" < "3.2" ]]' \
if '~/.config/tmux/check-version.sh "<" 3.2' \
'set-option -as terminal-features ",xterm*:Tc"' \
'set-option -as terminal-features ",xterm*:RGB"'
@@ -49,7 +49,7 @@ unbind -T copy-mode-vi MouseDragEnd1Pane
# Enable changing cursor shape per pane, vim or zsh should emit escape
# sequences to change cursor shape.
# iTerm2 only requires this before tmux 2.9
if '[ -n $ITERM_PROFILE ] && [[ "$TMUX_VERSION" < "2.9" ]]' \
if '[ -n $ITERM_PROFILE ] && ~/.config/tmux/check-version.sh "<" 2.9' \
'set -ga terminal-overrides "*:Ss=\E]1337;CursorShape=%p1%d\7"'
# VTE compatible terminals.
if '[ ! -n $ITERM_PROFILE ]' \
@@ -66,15 +66,19 @@ bind a run-shell ~/.local/share/tmux/layouts/window-auto
if -b '[ "`uname`" = "Darwin" ]' \
'set -g default-command "reattach-to-user-namespace -l $SHELL"'
if -b '[ "$TMUX_VERSION" > "3.2" ]' {
# Open a popup with running the default shell
bind T display-popup -S fg=#54546D -b rounded -d '#{pane_current_path}' -E $SHELL
# Open a popup with session creator/switcher
bind S display-popup -B -w 60 -h 10 -E '~/.config/tmux/session.sh'
# Open a popup with project selector
bind P display-popup -B -w 60 -h 10 -E '~/.config/tmux/project.sh'
# Open a popup to pick an interpreter then launch it
if -b '~/.config/tmux/check-version.sh ">=" 3.2 && ~/.config/tmux/check-version.sh "<" 3.3' {
bind A display-popup -d '#{pane_current_path}' -w 75% -h 90% -E ~/.config/tmux/agent.sh
bind I display-popup -d '#{pane_current_path}' -E '$SHELL -i ~/.config/tmux/interpreter.sh'
bind P display-popup -w 60 -h 10 -E '~/.config/tmux/project.sh'
bind S display-popup -w 60 -h 10 -E '~/.config/tmux/session.sh'
bind T display-popup -d '#{pane_current_path}' -E $SHELL
}
if -b '~/.config/tmux/check-version.sh ">=" 3.3' {
bind A display-popup -S fg=#54546D -b rounded -d '#{pane_current_path}' -w 75% -h 90% -E ~/.config/tmux/agent.sh
bind I display-popup -S fg=#54546D -b rounded -d '#{pane_current_path}' -E '$SHELL -i ~/.config/tmux/interpreter.sh'
bind P display-popup -B -w 60 -h 10 -E '~/.config/tmux/project.sh'
bind S display-popup -B -w 60 -h 10 -E '~/.config/tmux/session.sh'
bind T display-popup -S fg=#54546D -d '#{pane_current_path}' -E $SHELL
}
# Restore old next/previous window bindings