원클릭으로
tmux
Use tmux instead of bash tool to run commands that take more than ~30 seconds, like bulk operations, db migrations, dev servers.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Use tmux instead of bash tool to run commands that take more than ~30 seconds, like bulk operations, db migrations, dev servers.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
| name | tmux |
| description | Use tmux instead of bash tool to run commands that take more than ~30 seconds, like bulk operations, db migrations, dev servers. |
Use tmux when running long commands. Do not background with nohup or &
from the bash tool.
The point of this skill is to produce a session the user can attach to and actually see. If nobody ever needs to look, there is no reason to involve tmux at all. That is why most rules below protect one thing: what shows up on screen when the user attaches.
tmux new-session ... # correct: default socket
tmux -L agent new-session ... # wrong: never appears in the user's `tmux ls`
tmux -f /dev/null ... # wrong: throws away the user's tmux.conf
A session created on a private socket (-L / -S) is alive and healthy and
completely invisible to the user's tmux ls. This is the most common cause
of "the agent said it started a session but there is nothing there." Short of
digging through ps, the user has no way to find it.
Do not use -f /dev/null either. The user's tmux.conf carries scrollback size,
copy-mode keys, and status-bar identity. Discarding it drops history-limit to
the 2000-line default, so the very output you meant to show gets truncated.
Private sockets are for diagnosis only — find-sessions.sh --all and
wait-for-text.sh -L|-S can look at them. Never create on one.
W=$(tmux display -p '#{client_width}' 2>/dev/null); H=$(tmux display -p '#{client_height}' 2>/dev/null)
tmux new-session -d -x "${W:-200}" -y "${H:-50}" -s <name> '<command>' \; set-option -t <name> remain-on-exit on
tmux pipe-pane -o -t <name> 'cat >> /tmp/agent-tmux-<name>.log'
Each piece prevents one specific failure:
| Piece | What happens without it |
|---|---|
-x / -y | The session is born 80x24. Wide output and TUIs render truncated |
\; set-option ... remain-on-exit on | The session vanishes entirely the moment the command finishes or fails |
pipe-pane | (if you use a redirect instead) the pane renders blank — see the trap below |
Chaining with \; matters. Issuing set-option as a separate tmux call leaves
a window where the command can die first. One round trip has no race.
With remain-on-exit on the session survives its command and the pane shows
Pane is dead (status 127, ...), so the exit status is visible and a
failure is diagnosable. The tradeoff: it will not clean itself up, so kill it
once you have read the result.
Naming: descriptive, e.g. dev-server, nix-build, deploy.
# wrong: session is alive, but attaching shows an empty screen
tmux new-session -d -s build 'make > /tmp/agent-tmux-build.log 2>&1'
> log 2>&1 sends stdout/stderr entirely to the file. Nothing is ever drawn
in the pane. The user attaches and sees a blank screen — indistinguishable from
a dead session.
pipe-pane -o instead copies what the pane renders, so you get both the
screen and the log. Use it rather than a redirect.
A double redirect is worse. If the script already redirects internally with
> "$LOG" 2>&1 and the tmux command redirects again, the outer log file ends up
0 bytes: blank screen, empty log at the path you told the user about, and
the session gone once the build finishes. A 25 MB build log went completely
unseen this way. So when you move work into a script, do not redirect inside
the script — let pipe-pane handle it. If the script already writes its own
log, tell the user that path.
The command argument to tmux new-session must be a single line. A literal
\n gets fused into errors like bashncd: command not found. Nesting tmux +
bash -c + nix run makes escaping grow exponentially.
# wrong: literal newline fuses "bash" and "cd" into "bashncd"
tmux new-session -d -s build "bash\ncd ~/repos\nmake"
# wrong: bash -c inside bash -c, triple escaping
tmux new-session -d -s build "bash -c \"nix run .#yocto -- -c \\\"make\\\"\""
# correct 1: chain with && on one line (safest)
tmux new-session -d -x "${W:-200}" -y "${H:-50}" -s build 'cd ~/repos && make' \; set-option -t build remain-on-exit on
# correct 2: move it into a script when nesting is deep (preferred)
cat > /tmp/agent-tmux-build.sh << 'SCRIPT'
#!/usr/bin/env bash
set -euo pipefail
cd ~/repos/3rd/yocto
nix run .#yocto -- -c "bitbake core-image-weston"
SCRIPT
chmod +x /tmp/agent-tmux-build.sh
tmux new-session -d -x "${W:-200}" -y "${H:-50}" -s build '/tmp/agent-tmux-build.sh' \; set-option -t build remain-on-exit on
tmux pipe-pane -o -t build 'cat >> /tmp/agent-tmux-build.log'
Rule: two levels of nesting or more, move it to a script file. Getting a script right is faster and safer than getting the escaping right, and failures are easier to debug. Just remember the double-redirect trap above: the script must not redirect its own output.
Starting an agent CLI, REPL, or TUI in a session follows different rules.
W=$(tmux display -p '#{client_width}' 2>/dev/null); H=$(tmux display -p '#{client_height}' 2>/dev/null)
tmux new-session -d -x "${W:-200}" -y "${H:-50}" -s <name> -c <repo> '<agent-cli> [flags] "<initial prompt>"'
pipe-pane either — it records every repaint, so the log fills with control
sequences and is unreadable (top produced 8 KB in 4 seconds, roughly
7 MB/hour of escape codes).remain-on-exit. An interactive process stays alive on its own.-x/-y to the user's window. A TUI born at 80x24 renders cramped.-c <repo> so the working directory is exact.Read it with capture-pane; send further input with send-keys (see below).
Immediately after starting a session, give the user the commands to watch it:
# live
tmux attach -t <name>
# detach: Ctrl+b d
# one-shot read
tmux capture-pane -p -J -t <name> -S -200
# follow the log (when pipe-pane is attached)
tail -f /tmp/agent-tmux-<name>.log
# default socket
tmux ls
# detailed, with name filtering
{baseDir}/scripts/find-sessions.sh
{baseDir}/scripts/find-sessions.sh -q nix
# diagnosis: include sessions hidden on other sockets (never create on one)
{baseDir}/scripts/find-sessions.sh --all
Long-running processes — read the log file; it outlives the process:
tail -100 /tmp/agent-tmux-<name>.log
Interactive tools and TUIs (REPL, prompts, agent CLIs):
tmux capture-pane -p -J -t <name> -S -200
Wait ~0.5s after starting before reading. Agent CLIs take several seconds to produce a first response.
# literal text, no shell expansion
tmux send-keys -t <name> -l -- "input text"
tmux send-keys -t <name> Enter
# control keys
tmux send-keys -t <name> C-c
tmux send-keys -t <name> C-d
Rule: -l for literal text, key names for control keys, Enter as its own
argument. When targeting a TUI, leave ~0.5s between the -l send and Enter
so the input is committed.
:0.0 as the targetsession:0.0 is only correct when window and pane numbering start at zero.
Under a config with base-index 1 / pane-base-index 1 — common, and the case
here — the first pane is session:1.1, so :0.0 refers to nothing and every
read silently times out while the pane plainly shows the text you asked for.
Pass the session name alone. It resolves to the active pane regardless of numbering:
tmux capture-pane -p -J -t <name> -S -200 # correct
tmux capture-pane -p -J -t <name>:0.0 -S -200 # wrong under base-index 1
If you truly need a specific pane, get its real coordinates first:
tmux list-panes -t <name> -F '#{session_name}:#{window_index}.#{pane_index}'
Wait for a REPL prompt before sending the next input:
# Python prompt
{baseDir}/scripts/wait-for-text.sh -t <name> -p '^>>>' -T 15
# a specific message (fixed string)
{baseDir}/scripts/wait-for-text.sh -t <name> -p 'Server started' -F -T 30
# widen the search when the pane has a lot of scrollback
{baseDir}/scripts/wait-for-text.sh -t <name> -p 'Done' -T 60 -l 4000
On timeout it prints the last captured output to stderr. If the text is visibly on screen and the wait still times out, the target is wrong — see the trap above.
tmux kill-session -t <name>
With remain-on-exit on the session outlives its command, so clean up once you
have read the result. Left alone, the user's tmux ls fills with dead sessions.
-L, -S, or -f /dev/nullpipe-pane -o to keep both screen and log-x/-y from the user's window (80x24 truncates)\; set-option -t <name> remain-on-exit on, same round trip:0.0; it is not pane 1 under
base-index 1tmux ls first to avoid name collisionssend-keys -l --, Enter separatelywait-for-text.shBefore reporting that a session started, read the screen once with
capture-pane. Report it only after seeing that the session is alive and
something is on it.
Google Workspace + Search Console all-in-one CLI (gog). Calendar, Gmail, Drive, Tasks, Chat, Contacts, Sheets, Docs, Search Console, YouTube, Photos, Meet, and more. Single binary. Source: steipete/gogcli (upstream).
GLG의 시간축 관측소 (범용 스킬) — 어느 세션·리포에서든 GLG의 하루를 깊이 0/1/2/3(시간블록·저널헤딩·에이전트스탬프·커밋/노트)으로 한 KST 축 위에 세운다. scripts/collect.py로 LOCAL FULL(events.jsonl, 로컬·gitignore)을 만들고 query.py로 꺼낸다. 스크립트는 HOME 앵커(~/repos·~/org·lifetract·agenda)라 CWD와 무관하게 돈다. 이 스킬을 읽지 않고 축을 건드리면 계약을 다시 도출하다 틀린다 — 고정 KST, 반개방 [from,to), 자정은 시작일 귀속, 0건은 ok가 아니다, sqlite를 직접 열지 않는다, 코멘트는 어떤 projection에도 나가지 않는다. Triggers: timeline, 관측소, 시간축, 깊이 0, depth axis, collect.py, query.py, events.jsonl, LOCAL FULL, 하루 slice, 주간 판독, 도메인 분포, projection, 표현, 증거, provenance, 커밋 수, 저널 일수, 사실 확인.
Incrementally embed sessions only — near-live. On call, new sessions land in semantic-memory immediately. OpenRouter Qwen3-Embedding-8B 4096d, paid remote but ~$0.000–0.001 for a few recent sessions. dim 4096 preflight → API-0 exit when to_index=0. Use before a new session or when recent-session recall feels stale. '/memory-sync', 'memory sync', 'session embedding', '세션 임베딩', '세션 증분', '기억 최신화'.
Semantic search over past sessions (pi + Claude Code) and the public digital garden md index (andenken md.lance, OpenRouter Qwen3-Embedding-8B 4096d). Uses LanceDB + hybrid retrieval (vector + FTS with score normalization). Korean↔English cross-lingual via dictcli expand. Recall tracking for memory consolidation. Use when searching for past conversations, decisions, context, or garden knowledge concepts.
Emacs daemon — org manipulation, denote notes, citar bibliography, org-agenda, arbitrary elisp. Two sockets: server (agent work), user (show file to user). Core: agent-denote-add-history(ID,CONTENT), agent-denote-add-heading(ID,TITLE,BODY) or (ID,TITLE,TAG,BODY) — no tag? body as 3rd arg. Never pass nil. agent-denote-add-link(ID,TARGET-ID,DESC). All 3 args required.
Search/view local BibTeX entries. If only a source URL exists, use zotero-config (`server start` → `save` → `bib sync` → `bibcli search/show`) to recover a citation key before leaving orphan `#+print_bibliography:`.