From nx
Use when controlling interactive CLI applications, debugging with pdb/gdb/jshell, spawning Claude Code instances, or working with REPLs and long-running processes
npx claudepluginhub hellblazer/nexus --plugin nxThis skill uses the workspace's default tool permissions.
This is a **standalone skill** that provides workflows and best practices for interactive CLI control using raw tmux commands. No additional tools required — just tmux on PATH.
Provides UI/UX resources: 50+ styles, color palettes, font pairings, guidelines, charts for web/mobile across React, Next.js, Vue, Svelte, Tailwind, React Native, Flutter. Aids planning, building, reviewing interfaces.
Fetches up-to-date documentation from Context7 for libraries and frameworks like React, Next.js, Prisma. Use for setup questions, API references, and code examples.
Analyzes competition with Porter's Five Forces, Blue Ocean Strategy, and positioning maps to identify differentiation opportunities and market positioning for startups and pitches.
Share bugs, ideas, or general feedback.
This is a standalone skill that provides workflows and best practices for interactive CLI control using raw tmux commands. No additional tools required — just tmux on PATH.
For simple one-off commands, use the regular Bash tool instead.
All operations use tmux directly via Bash. Key commands:
| Operation | Command |
|---|---|
| Launch shell in new pane | tmux split-window -h -P -F '#{session_name}:#{window_index}.#{pane_index}' zsh |
| Send text + Enter | tmux send-keys -t $PANE 'command here' Enter |
| Send text without Enter | tmux send-keys -t $PANE 'partial text' |
| Capture pane output | tmux capture-pane -t $PANE -p |
| Capture last N lines | tmux capture-pane -t $PANE -p -S -50 |
| Kill pane | tmux kill-pane -t $PANE |
| List panes | tmux list-panes -F '#{pane_index} #{pane_current_command} #{pane_id}' |
| Send Ctrl+C | tmux send-keys -t $PANE C-c |
| Send Escape | tmux send-keys -t $PANE Escape |
Panes are addressed as session:window.pane:
myapp:1.2 — session "myapp", window 1, pane 2split-window -P -F '#{session_name}:#{window_index}.#{pane_index}'CRITICAL: Never launch commands directly — always launch a shell first.
# CORRECT - Launch shell, then run command inside it
PANE=$(tmux split-window -h -P -F '#{session_name}:#{window_index}.#{pane_index}' zsh)
sleep 0.5
tmux send-keys -t "$PANE" 'python script.py' Enter
# WRONG - Direct command (if it errors, pane closes and you lose output!)
tmux split-window -h 'python script.py'
Poll until pane output stabilizes (no changes for N seconds):
# Wait for pane to become idle (no output changes for 2 seconds)
wait_idle() {
local pane="$1" idle_secs="${2:-2}" timeout="${3:-60}"
local last_hash="" hash="" start=$(date +%s) last_change=$(date +%s)
while true; do
local now=$(date +%s)
(( now - start > timeout )) && return 1
hash=$(tmux capture-pane -t "$pane" -p | md5sum | cut -d' ' -f1)
if [[ "$hash" != "$last_hash" ]]; then
last_hash="$hash"; last_change=$now
elif (( now - last_change >= idle_secs )); then
return 0
fi
sleep 0.5
done
}
# Usage:
tmux send-keys -t "$PANE" 'long_running_command' Enter
wait_idle "$PANE" 2 60
output=$(tmux capture-pane -t "$PANE" -p)
For commands where you need the exit code, use marker-based execution:
# Execute and capture exit code
tmux_execute() {
local pane="$1" cmd="$2" timeout="${3:-30}"
local marker="__EXEC_${RANDOM}_$(date +%s)__"
tmux send-keys -t "$pane" "echo ${marker}_START; { ${cmd}; } 2>&1; echo ${marker}_END:\$?" Enter
local start=$(date +%s)
while (( $(date +%s) - start < timeout )); do
local captured=$(tmux capture-pane -t "$pane" -p -S -500)
if [[ "$captured" == *"${marker}_END:"* ]]; then
local exit_code=$(echo "$captured" | grep -o "${marker}_END:[0-9]*" | sed "s/${marker}_END://")
local output=$(echo "$captured" | sed -n "/${marker}_START/,/${marker}_END/p" | sed '1d;$d')
echo "EXIT_CODE=$exit_code"
echo "$output"
return "$exit_code"
fi
sleep 0.5
done
echo "EXIT_CODE=-1 (timeout)"
return 1
}
# Usage:
tmux_execute "$PANE" "pytest tests/" 120
output=$(tmux capture-pane -t "$PANE" -p)
tmux kill-pane -t "$PANE"
PANE=$(tmux split-window -h -P -F '#{session_name}:#{window_index}.#{pane_index}' zsh)
sleep 0.5
tmux send-keys -t "$PANE" 'python -m pdb script.py' Enter
sleep 1
tmux send-keys -t "$PANE" 'b function_name' Enter # breakpoint
tmux send-keys -t "$PANE" 'c' Enter # continue
tmux send-keys -t "$PANE" 'p variable' Enter # print
sleep 0.5
output=$(tmux capture-pane -t "$PANE" -p)
tmux send-keys -t "$PANE" 'q' Enter
sleep 0.3
tmux kill-pane -t "$PANE"
Two gotchas specific to Claude Code:
Enter.⎇ main, context %, bypass permissions on). The standard hash-based wait_idle will never settle on "no changes" while Claude is actively generating. Instead, poll for content markers (specific strings you expect in the response) rather than waiting for the output to stop changing.PANE=$(tmux split-window -h -P -F '#{session_name}:#{window_index}.#{pane_index}' zsh)
sleep 0.5
tmux send-keys -t "$PANE" 'cd /path/to/project && claude --dangerously-skip-permissions' Enter
# Wait for splash to clear (Claude idle at ❯ prompt)
sleep 5
# Type prompt text into the input field (no Enter yet)
tmux send-keys -t "$PANE" 'Analyze performance in src/processor.py'
sleep 0.3
# Now send Enter — splash is gone, this goes to Claude
tmux send-keys -t "$PANE" Enter
# Poll for content markers instead of idle detection
for i in $(seq 1 60); do
sleep 2
output=$(tmux capture-pane -t "$PANE" -p -S -100)
# Replace with strings you expect to appear in the response
if echo "$output" | grep -qE "hotspot|bottleneck|O\(n\)|performance|✓|✗"; then
break
fi
done
analysis=$(tmux capture-pane -t "$PANE" -p -S -200)
# Exit Claude cleanly
tmux send-keys -t "$PANE" C-c
sleep 0.3
tmux send-keys -t "$PANE" '/exit' Enter
sleep 1
tmux kill-pane -t "$PANE"
PANE=$(tmux split-window -h -P -F '#{session_name}:#{window_index}.#{pane_index}' zsh)
sleep 0.5
tmux send-keys -t "$PANE" 'python3' Enter
sleep 1
tmux send-keys -t "$PANE" 'import requests' Enter
tmux send-keys -t "$PANE" "response = requests.get('https://api.example.com')" Enter
wait_idle "$PANE" 2 30
output=$(tmux capture-pane -t "$PANE" -p)
tmux send-keys -t "$PANE" 'exit()' Enter
sleep 0.3
tmux kill-pane -t "$PANE"
PANE=$(tmux split-window -h -P -F '#{session_name}:#{window_index}.#{pane_index}' zsh)
sleep 0.5
tmux send-keys -t "$PANE" 'mvn clean install' Enter
for i in {1..30}; do
sleep 10
output=$(tmux capture-pane -t "$PANE" -p -S -50)
[[ "$output" =~ "BUILD SUCCESS" ]] && break
[[ "$output" =~ "BUILD FAILURE" ]] && break
done
final=$(tmux capture-pane -t "$PANE" -p)
tmux kill-pane -t "$PANE"
PANE=$(tmux split-window -h -P -F '#{session_name}:#{window_index}.#{pane_index}' zsh)
[[ -z "$PANE" ]] && { echo "Failed to launch pane"; return 1; }
sleep 0.5
tmux send-keys -t "$PANE" 'risky_command' Enter
wait_idle "$PANE" 2 30
output=$(tmux capture-pane -t "$PANE" -p)
if [[ "$output" =~ "error" ]]; then
echo "Failed: $output"
tmux kill-pane -t "$PANE"
return 1
fi
tmux kill-pane -t "$PANE"
| Problem | Solution |
|---|---|
| Pane closed unexpectedly | Always launch shell first |
| Missing output | Use wait_idle or longer sleep before capture |
| Commands not executing | Add sleep/wait_idle between commands |
| Timeout waiting | Increase timeout or check if process is stuck |
| Enter key not received | Add sleep 0.3 between send-keys calls |
| Claude ignores first prompt | Splash screen absorbs the first Enter — type prompt, wait for splash to clear (sleep 5), then send bare Enter |
| Claude idle detection never settles | Footer lines (branch, context %, bypass mode) change constantly — poll for expected response content strings instead of hash-based idle |
wait_idle instead of sleep for timing-S -N with capture-pane for long outputs (e.g., -S -500 for 500 lines)tmux list-panes to see all active panestmux send-keys -t "$PANE" (targets contain colons/dots)Session Scratch (T1): mcp__plugin_nx_nexus__scratch(action="put", content="..." to note interactive session findings (pane IDs, interim output, working hypotheses). Flagged items auto-promote to T2 at session end.