tmux shared session workflow (ops)
Rule: Any request that involves running commands on the host uses this workflow. Sudo is always assumed required. Use the tmux_ops tool to pick session and probe; do not infer busy from tmux ls or parse raw capture yourself.
Tool (use this)
- Entrypoint: From repo root run
./_localsetup/tools/tmux_ops (or set REMOTE_TMUX_HOST to run the same tool on a remote host via SSH; see Remote below).
- Pick session:
./_localsetup/tools/tmux_ops pick → JSON e.g. {"session": "ops", "reason": "idle"} or {"reason": "created"} or {"reason": "waiting_sudo"}. Use that session for the whole run.
- Probe sudo:
./_localsetup/tools/tmux_ops probe -t <session> → JSON {"sudo": "ready"} or {"sudo": "password_required"}.
- Send command:
./_localsetup/tools/tmux_ops send -t <session> '...' sends the command and applies a short pylon-guard delay (default 0.5 s) to prevent commands racing ahead of output on high-latency links. Does not wait for the command to finish unless --wait is passed.
- Send and wait:
./_localsetup/tools/tmux_ops send -t <session> --wait '...' sends and then polls for idle. Returns the moment the prompt reappears. Use for commands expected to finish in < 30 s.
- Wait (standalone):
./_localsetup/tools/tmux_ops wait -t <session> [--timeout N] polls pane for idle. Use after send (without --wait) for long-running ops. Returns {"idle": true, "elapsed_s": X, "polls": N} or {"idle": false, "timed_out": true, "cursor_line": "..."}.
- Idle definition: Idle = cursor line matches a shell prompt (
$ or #) AND cursor Y moved from its pre-send position (cursor-delta guard prevents false positives).
Subcommand reference
| Subcommand |
Key args |
Returns |
pick |
|
{session, reason} |
probe -t SESSION |
|
{sudo: ready|password_required|unknown} |
send -t SESSION CMD |
--delay, --wait, --wait-timeout, --idle-re |
{sent, delay_s[, idle, elapsed_s, polls, timed_out, cursor_line]} |
wait -t SESSION |
--timeout, --idle-re, --pre-cursor-y |
{idle, elapsed_s, polls[, timed_out, cursor_line]} |
Optional: --idle-re PATTERN overrides the prompt regex (also env TMUX_OPS_IDLE_RE). --pre-cursor-y N enables the cursor-delta guard on standalone wait calls.
Sequence (follow exactly)
Pick session. Run ./_localsetup/tools/tmux_ops pick. Parse JSON; use the returned session for the whole run. If the tool errors, report and stop.
Show attach command immediately. Right after pick (whether the session was created or already existed), display the join command in a copy-paste code block so the user can attach at any time. Do not wait for the user to confirm they joined.
- Put this in a fenced code block:
tmux new-session -A -s <session>
- If pick returned
reason: "waiting_sudo": cancel any abandoned command first: send tmux send-keys -t <session> C-c, wait ~0.5 s, then run the probe.
- Then run the probe.
Gate on probe only. If the probe returns "sudo": "password_required": stop. Ask the user to attach to the session, enter the password in that pane, and reply "sudo ready". Only after they reply may you send the first command. If "sudo": "ready", continue to step 4.
Send one step at a time. Send one logical step at a time.
- Short command (< 30 s expected): use
send --wait. Returns the moment idle is confirmed; no sleep needed.
- Long command (builds, installs, deploys): use
send (no --wait), then call wait --timeout N where N is your best estimate.
idle=true → continue to next step.
timed_out=true → inspect cursor_line field; extend wait, read log, or escalate.
- Never use arbitrary
sleep calls as a completion signal.
- Use a log:
/tmp/agent-<session>-YYYYmmdd-HHMMSS.log (e.g. |& tee -a $LOG). Read the log yourself.
Run. Commands go via ./_localsetup/tools/tmux_ops send -t <session> '...'. Use a log path. Never run those commands in the agent shell.
Re-gate if sudo expires. If a later command fails (e.g. sudo timeout), run the probe again; if password_required, stop and ask for "sudo ready"; only then continue.
Waiting strategy
After send, choose:
Command expected in < 30 s?
YES → send --wait
Returns idle=true the moment done. Continue immediately.
Command expected in > 30 s or unknown?
→ send (no --wait), then wait --timeout N
idle=true → continue
timed_out=true → inspect cursor_line; extend wait, read log, or escalate
Need exact latency, zero polling (advanced)?
→ append "; tmux wait-for -S done-$$" to command
then run: tmux wait-for done-<PID>
Blocks until shell signals completion. Use only when you fully control the command string.
Sentinel PS1 (advanced, zero false positives)
If you want completely unambiguous idle detection, inject a known prompt right after pick:
tmux_ops send -t ops 'export PS1="__OPS__\$ "'
# Then pass --idle-re '^__OPS__[$#]\s*$' to all subsequent wait calls
This makes the prompt unique and removes any chance of a false match on command output.
Remote (VMs, remote SSH, Docker)
When the tmux server runs on a different host (e.g. Cursor on laptop, tmux on VM or remote server):
- Set REMOTE_TMUX_HOST to that host (e.g.
export REMOTE_TMUX_HOST=sh0t). Optionally REMOTE_TMUX_CWD to the repo path on the remote (default /opt/devzone/devops).
- Run
tmux_ops pick, probe, send, and wait as usual; the wrapper runs the tool over SSH and returns the same JSON.
Checklist
| Step |
Do |
Do not |
| 1 |
Run tmux_ops pick; use returned session for whole run. |
Infer session from tmux ls or "(attached)". |
| 2 |
Right after pick, show attach in code block. If reason: waiting_sudo, send C-c first, then probe. |
Wait for "I joined"; skip cancel when waiting_sudo. |
| 3 |
If probe says password_required, stop and ask user; only then send commands. If ready, continue. |
Send commands before user says "sudo ready" when probe said password_required. |
| 4 (short cmd) |
send --wait; continue on idle=true. |
Sleep then assume done. |
| 4 (long cmd) |
send, then wait --timeout N; handle timed_out. |
Guess a sleep duration. |
| 5 |
Run in chosen session via tmux_ops send; tee to log; read log. |
Run in agent shell; skip log. |
| 6 |
If sudo expired, probe again; if password_required, stop. |
Assume sudo still valid. |
Session and log
- Session: the one returned by
tmux_ops pick (e.g. ops or ops1).
- Log:
/tmp/agent-<session>-YYYYmmdd-HHMMSS.log. Commands: ... |& tee -a $LOG. After run: read log (e.g. tail -n 200 that file).
Hard rules
- Server/ops commands run only in tmux. Use
tmux_ops to pick session and probe; never in the agent shell.
- If probe returns ready, continue immediately; do not stop for a chat "sudo ready". Only stop when probe returns
password_required.
- If
password_required: ask user to enter password in the ops pane and reply "sudo ready"; then proceed.
- Right after pick, display the attach command in a copy-paste code block; do not wait for user to confirm join before probing.
- Capture output to the log path; the agent reads the log. State what you are about to do before running commands.
- Never use
sleep as a completion signal. Use send --wait or wait --timeout N.
1---2name: localsetup-tmux-shared-session-workflow3description: Server/ops in tmux; use tmux_ops tool to pick session (idle = prompt on current line) and probe sudo; run commands only in chosen session. Supports REMOTE_TMUX_HOST for VMs/remote/Docker.4---5
6# tmux shared session workflow (ops)
7
8**Rule:** Any request that involves running commands on the host uses this workflow. Sudo is always assumed required. Use the **tmux_ops** tool to pick session and probe; do not infer busy from `tmux ls` or parse raw capture yourself.
9
10## Tool (use this)
11
12- **Entrypoint:** From repo root run `./_localsetup/tools/tmux_ops` (or set `REMOTE_TMUX_HOST` to run the same tool on a remote host via SSH; see Remote below).
13- **Pick session:** `./_localsetup/tools/tmux_ops pick` → JSON e.g. `{"session": "ops", "reason": "idle"}` or `{"reason": "created"}` or `{"reason": "waiting_sudo"}`. Use that `session` for the whole run.
14- **Probe sudo:** `./_localsetup/tools/tmux_ops probe -t <session>` → JSON `{"sudo": "ready"}` or `{"sudo": "password_required"}`.
15- **Send command:** `./_localsetup/tools/tmux_ops send -t <session> '...'` sends the command and applies a short pylon-guard delay (default 0.5 s) to prevent commands racing ahead of output on high-latency links. Does **not** wait for the command to finish unless `--wait` is passed.
16- **Send and wait:** `./_localsetup/tools/tmux_ops send -t <session> --wait '...'` sends and then polls for idle. Returns the moment the prompt reappears. Use for commands expected to finish in < 30 s.
17- **Wait (standalone):** `./_localsetup/tools/tmux_ops wait -t <session> [--timeout N]` polls pane for idle. Use after `send` (without `--wait`) for long-running ops. Returns `{"idle": true, "elapsed_s": X, "polls": N}` or `{"idle": false, "timed_out": true, "cursor_line": "..."}`.
18- **Idle definition:** Idle = cursor line matches a shell prompt (`$` or `#`) AND cursor Y moved from its pre-send position (cursor-delta guard prevents false positives).
19
20### Subcommand reference
21
22| Subcommand | Key args | Returns |
23|---|---|---|
24| `pick` | | `{session, reason}` |
25| `probe -t SESSION` | | `{sudo: ready\|password_required\|unknown}` |
26| `send -t SESSION CMD` | `--delay`, `--wait`, `--wait-timeout`, `--idle-re` | `{sent, delay_s[, idle, elapsed_s, polls, timed_out, cursor_line]}` |
27| `wait -t SESSION` | `--timeout`, `--idle-re`, `--pre-cursor-y` | `{idle, elapsed_s, polls[, timed_out, cursor_line]}` |
28
29Optional: `--idle-re PATTERN` overrides the prompt regex (also env `TMUX_OPS_IDLE_RE`). `--pre-cursor-y N` enables the cursor-delta guard on standalone `wait` calls.
30
31## Sequence (follow exactly)
32
331. **Pick session.** Run `./_localsetup/tools/tmux_ops pick`. Parse JSON; use the returned `session` for the whole run. If the tool errors, report and stop.
34
352. **Show attach command immediately.** Right after pick (whether the session was created or already existed), display the join command in a **copy-paste code block** so the user can attach at any time. Do not wait for the user to confirm they joined.
36 - Put this in a fenced code block:
37 `tmux new-session -A -s <session>`
38 - **If pick returned `reason: "waiting_sudo"`:** cancel any abandoned command first: send `tmux send-keys -t <session> C-c`, wait ~0.5 s, then run the probe.
39 - Then run the probe.
40
413. **Gate on probe only.** If the probe returns `"sudo": "password_required"`: stop. Ask the user to attach to the session, enter the password in that pane, and reply "sudo ready". Only after they reply may you send the first command. If `"sudo": "ready"`, continue to step 4.
42
434. **Send one step at a time.** Send one logical step at a time.
44 - **Short command (< 30 s expected):** use `send --wait`. Returns the moment idle is confirmed; no sleep needed.
45 - **Long command (builds, installs, deploys):** use `send` (no `--wait`), then call `wait --timeout N` where N is your best estimate.
46 - `idle=true` → continue to next step.
47 - `timed_out=true` → inspect `cursor_line` field; extend wait, read log, or escalate.
48 - **Never use arbitrary `sleep` calls as a completion signal.**
49 - Use a log: `/tmp/agent-<session>-YYYYmmdd-HHMMSS.log` (e.g. `|& tee -a $LOG`). Read the log yourself.
50
515. **Run.** Commands go via `./_localsetup/tools/tmux_ops send -t <session> '...'`. Use a log path. Never run those commands in the agent shell.
52
536. **Re-gate if sudo expires.** If a later command fails (e.g. sudo timeout), run the probe again; if `password_required`, stop and ask for "sudo ready"; only then continue.
54
55## Waiting strategy
56
57```
58After send, choose:
59
60 Command expected in < 30 s?
61 YES → send --wait
62 Returns idle=true the moment done. Continue immediately.
63
64 Command expected in > 30 s or unknown?
65 → send (no --wait), then wait --timeout N
66 idle=true → continue
67 timed_out=true → inspect cursor_line; extend wait, read log, or escalate
68
69 Need exact latency, zero polling (advanced)?
70 → append "; tmux wait-for -S done-$$" to command
71 then run: tmux wait-for done-<PID>
72 Blocks until shell signals completion. Use only when you fully control the command string.
73```
74
75### Sentinel PS1 (advanced, zero false positives)
76
77If you want completely unambiguous idle detection, inject a known prompt right after `pick`:
78
79```bash
80tmux_ops send -t ops 'export PS1="__OPS__\$ "'
81# Then pass --idle-re '^__OPS__[$#]\s*$' to all subsequent wait calls
82```
83
84This makes the prompt unique and removes any chance of a false match on command output.
85
86## Remote (VMs, remote SSH, Docker)
87
88When the tmux server runs on a different host (e.g. Cursor on laptop, tmux on VM or remote server):
89
90- Set **REMOTE_TMUX_HOST** to that host (e.g. `export REMOTE_TMUX_HOST=sh0t`). Optionally **REMOTE_TMUX_CWD** to the repo path on the remote (default `/opt/devzone/devops`).
91- Run `tmux_ops pick`, `probe`, `send`, and `wait` as usual; the wrapper runs the tool over SSH and returns the same JSON.
92
93## Checklist
94
95| Step | Do | Do not |
96|------|-----|--------|
97| 1 | Run `tmux_ops pick`; use returned session for whole run. | Infer session from `tmux ls` or "(attached)". |
98| 2 | Right after pick, show attach in code block. If `reason: waiting_sudo`, send C-c first, then probe. | Wait for "I joined"; skip cancel when `waiting_sudo`. |
99| 3 | If probe says `password_required`, stop and ask user; only then send commands. If ready, continue. | Send commands before user says "sudo ready" when probe said `password_required`. |
100| 4 (short cmd) | `send --wait`; continue on `idle=true`. | Sleep then assume done. |
101| 4 (long cmd) | `send`, then `wait --timeout N`; handle `timed_out`. | Guess a sleep duration. |
102| 5 | Run in chosen session via `tmux_ops send`; tee to log; read log. | Run in agent shell; skip log. |
103| 6 | If sudo expired, probe again; if `password_required`, stop. | Assume sudo still valid. |
104
105## Session and log
106
107- Session: the one returned by `tmux_ops pick` (e.g. `ops` or `ops1`).
108- Log: `/tmp/agent-<session>-YYYYmmdd-HHMMSS.log`. Commands: `... |& tee -a $LOG`. After run: read log (e.g. `tail -n 200` that file).
109
110## Hard rules
111
1121. Server/ops commands run only in tmux. Use `tmux_ops` to pick session and probe; never in the agent shell.
1132. If probe returns ready, continue immediately; do not stop for a chat "sudo ready". Only stop when probe returns `password_required`.
1143. If `password_required`: ask user to enter password in the ops pane and reply "sudo ready"; then proceed.
1154. Right after pick, display the attach command in a copy-paste code block; do not wait for user to confirm join before probing.
1165. Capture output to the log path; the agent reads the log. State what you are about to do before running commands.
1176. Never use `sleep` as a completion signal. Use `send --wait` or `wait --timeout N`.