Start a local Claude Code instance in tmux with interactive login...
A pre-warmed Claude Code TUI session runs in tmux, started automatically by the SessionStart hook.
# Send query using -l (literal) for reliability with special characters
tmux send-keys -t claude-local -l 'your query here' && tmux send-keys -t claude-local Enter
# Wait for response to stabilize, then capture (recommended)
.claude/skills/local-claude/scripts/tmux-poll-wait.sh claude-local
The polling script waits until output stabilizes (3 consecutive unchanged polls at 3s intervals) or times out after 60s, then outputs the final capture.
The basic capture includes scrollback history. To extract just the response for a specific command, use pattern matching:
QUERY="your query here"
# Escape special regex characters in query
SAFE_QUERY=$(printf '%s' "$QUERY" | sed 's/[.[\*^$()+?|\\]/\\&/g')
tmux capture-pane -t claude-local -p -S -100 \
| sed -n "/❯ $SAFE_QUERY/,/^❯ [a-z]/p" \
| head -n -1 \
| grep -v '^────' \
| grep -v '⏵⏵'
How it works:
. [ * ^ $ ( ) + ? | \) in the querysed -n "/❯ $SAFE_QUERY/,/^❯ [a-z]/p" - extract from your command prompt to the next commandhead -n -1 - remove the trailing "next command" linegrep -v - filter out UI chrome (separators, mode indicators)Tips:
-J to capture-pane to join wrapped lines: tmux capture-pane -t claude-local -p -J -S -100LC_ALL=C.UTF-8 before running to ensure consistent ❯ prompt matching across localesNote: If the same query appears multiple times in scrollback, this captures ALL occurrences. To get only the most recent response, use this awk-based extraction:
QUERY="your query here"
SAFE_QUERY=$(printf '%s' "$QUERY" | sed 's/[.[\*^$()+?|\\]/\\&/g')
tmux capture-pane -t claude-local -p -S -200 \
| awk -v q="$SAFE_QUERY" '
/^❯ / { printing = ($0 ~ "^❯ " q "$"); if (printing) { delete out; n=0 } next }
{ if (printing) {
if (/^❯ [a-z]/) { printing=0 }
else if ($0 !~ /^────/ && $0 !~ /⏵⏵/) { out[++n]=$0 }
}
}
END { for (i=1; i<=n; i++) print out[i] }'
Clear history first (captures everything since clear):
tmux clear-history -t claude-local
tmux send-keys -t claude-local -l 'query' && tmux send-keys -t claude-local Enter
sleep 15
tmux capture-pane -t claude-local -p -S -
Pipe to file (real-time capture, includes ANSI codes):
tmux pipe-pane -t claude-local 'cat > /tmp/output.txt'
tmux send-keys -t claude-local -l 'query' && tmux send-keys -t claude-local Enter
sleep 15
tmux pipe-pane -t claude-local # stop piping
# Strip ANSI escape codes for clean output
sed -r 's/\x1B\[[0-9;]*[A-Za-z]//g' /tmp/output.txt
| Action | Command |
|---|---|
| Check status | tmux capture-pane -t claude-local -p -S -30 |
| Send query | tmux send-keys -t claude-local -l 'text' && tmux send-keys -t claude-local Enter |
| Wait for response | .claude/skills/local-claude/scripts/tmux-poll-wait.sh claude-local |
| Wait (custom timeout) | .claude/skills/local-claude/scripts/tmux-poll-wait.sh claude-local 120 |
| Attach | tmux attach -t claude-local |
| Restart | tmux kill-session -t claude-local (will restart on next session) |
The .githooks/post-checkout hook (triggered by SessionStart) handles everything:
claude-local tmux session with --dangerously-skip-permissionsOnly runs in remote environments (CLAUDE_CODE_REMOTE=true).
The bundled scripts/tmux-poll-wait.sh intelligently waits for Claude to finish.
Requirements: tmux must be installed and the target session must exist.
Exit codes:
0 - Output stabilized (success)1 - Session not found124 - Timeout (like GNU timeout)Environment variables (optional):
| Variable | Default | Description |
|---|---|---|
TMUX_POLL_INTERVAL |
3 | Seconds between polls |
TMUX_POLL_STABLE |
3 | Consecutive unchanged polls required |
TMUX_POLL_LINES |
100 | Lines to capture for comparison |
# Usage
.claude/skills/local-claude/scripts/tmux-poll-wait.sh <session> [timeout_seconds]
# Examples
.claude/skills/local-claude/scripts/tmux-poll-wait.sh claude-local # 60s timeout
.claude/skills/local-claude/scripts/tmux-poll-wait.sh claude-local 120 # 120s timeout
# Check exit code to distinguish stable vs timeout
.claude/skills/local-claude/scripts/tmux-poll-wait.sh claude-local > output.txt
if [ $? -eq 124 ]; then echo "Timed out"; fi
Only needed if credentials are revoked:
tmux new-session -d -s claude-reauth "bash -c 'unset CLAUDE_CODE_REMOTE CLAUDE_CODE_ENTRYPOINT CLAUDECODE CLAUDE_CODE_SESSION_ID CLAUDE_CODE_REMOTE_SESSION_ID CLAUDE_CODE_OAUTH_TOKEN_FILE_DESCRIPTOR CLAUDE_CODE_WEBSOCKET_AUTH_FILE_DESCRIPTOR; exec claude /login'"
.claude/skills/local-claude/scripts/tmux-poll-wait.sh claude-reauth 30
# After user provides code:
tmux send-keys -t claude-reauth 'CODE_HERE' Enter
# Use base64 | tr -d '\n' for cross-platform compatibility (macOS/Linux)
gh variable set CLAUDE_CREDENTIALS --repo eastlondoner/claude --body "$(base64 ~/.claude/.credentials.json | tr -d '\n')"
Security note: For production use, consider using gh secret set instead of gh variable set for sensitive credentials, as secrets are encrypted and not visible in logs.