- frontmatter description: 'Terminate...mark it terminated' → 'Stop...hard=terminated, stop options=stopped' - heading: 'Multi-Agent Delete' → 'Multi-Agent Stop' - tags: delete → stop - state machine diagram: delete → stop - prose: soft/hard delete → soft/hard stop throughout - stop_session.sh: comments + echo 'delete complete' → 'stop complete' - create/SKILL.md: companion list 'delete' → 'stop' (2 locations) - preserved: status=terminated (valid YAML value), terminated_at field, --purge-conversation semantics
9.8 KiB
name, description, version, author, license, platforms, environments, metadata
| name | description | version | author | license | platforms | environments | metadata | ||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| tmux-agent-orchestrate-stop | Stop an agent tmux session (claude, antigravity/agy) and update .hermes/agent-sessions.yaml. Hard mode marks status=terminated; stop options (--capture-id/--reason/--graceful) mark status=stopped with conversation preserved for resume. Does NOT delete on-disk conversation artifacts (jsonl/db) — those are preserved unless --purge-conversation is passed. Use when ending a work session, switching to a different one, or cleaning up before a fresh start. | 1.0.0 | godopu | MIT |
|
|
|
Multi-Agent Stop — Stop an Agent tmux Session
Companion skills:
tmux-agent-orchestrate-create(start),tmux-agent-orchestrate-resume(re-attach),tmux-agent-orchestrate-monitor(live status). Tmux Isolation:stop명령은 YAML의tmux_server필드를 자동으로 파싱하여 해당 격리 서버의 세션을 안전하게 종료(kill)하므로,TMUX_SERVER_NAME환경변수를 수동으로 지정할 필요가 없습니다. Single source of truth:./.hermes/agent-sessions.yaml.
What this skill does
Stop an agent's tmux session and mark the YAML entry (terminated or stopped). Preserves:
- The tmux session's recorded
pane.pid / cmd / cwd / mcp_attachmentsfor audit - The agent's on-disk conversation (claude
*.jsonl, agyconversations/*.db) — so the user cantmux-agent-orchestrate-resumelater - The
start_commandso a futuretmux-agent-orchestrate-create --session <name>reproduces the same tmux spec
The user explicitly chooses:
- soft stop (default): update YAML only; leave tmux running. Useful when "stop" really means "I'm done with this card".
- hard stop:
tmux kill-session+ update YAML. The default when the user says "kill it" or "end the session".
Pre-flight
SESSION_NAME=<workspace>-creator-<agent> # convention
AGENT_SESSIONS_YAML=.hermes/agent-sessions.yaml
# 1) Session is registered?
python3 -c "
import yaml
d = yaml.safe_load(open('$AGENT_SESSIONS_YAML'))
names = [s['name'] for s in d.get('tmux_sessions', [])]
if '$SESSION_NAME' not in names:
print('NOT in YAML — refusing to stop (no audit trail). Use tmux-agent-orchestrate-create first, or pass --force-no-yaml.')
raise SystemExit(1)
"
# 2) Already terminated?
ALREADY=$(python3 -c "
import yaml
d = yaml.safe_load(open('$AGENT_SESSIONS_YAML'))
s = [x for x in d['tmux_sessions'] if x['name']=='$SESSION_NAME'][0]
print(s.get('status', 'unknown'))
")
if [ "$ALREADY" = "terminated" ]; then
echo "Already terminated at $(python3 -c "import yaml; d=yaml.safe_load(open('$AGENT_SESSIONS_YAML')); print([x for x in d['tmux_sessions'] if x['name']=='$SESSION_NAME'][0].get('terminated_at',''))")"
echo "Re-running will just refresh the timestamp. Continue? (--yes to skip)"
fi
Workflow
# 1. soft stop (YAML only — tmux left running)
bash skills/tmux-agent-orchestrate-stop/scripts/stop_session.sh \
--session "$SESSION_NAME" --mode soft
# 2. hard stop (default — kill tmux + update YAML)
bash skills/tmux-agent-orchestrate-stop/scripts/stop_session.sh \
--session "$SESSION_NAME" --mode hard
# 3. hard stop + clean up on-disk conversation (DANGEROUS)
# — this prevents any future resume. Use only when user is certain.
bash skills/tmux-agent-orchestrate-stop/scripts/stop_session.sh \
--session "$SESSION_NAME" --mode hard --purge-conversation
Stop extension (Option A — stop semantics without a 6th skill)
Rather than a separate tmux-agent-orchestrate-stop route, the base stop command absorbs the
"stop" intent via three opt-in options. Passing any of them switches the YAML
transition from terminated to stopped (running → stopped), signalling
"deliberately stopped, conversation preserved, ready to resume":
# Stop: capture the conversation id into the row, record a reason, exit gracefully.
bash skills/tmux-agent-orchestrate-stop/scripts/stop_session.sh \
--session "$SESSION_NAME" --capture-id --reason api_error --graceful
| Option | Effect |
|---|---|
--capture-id |
Before kill, resolve THIS workspace's conversation id via find_workspace_uuid (per-row → workspace-scoped disk scan → cache) and record it to claude_session_id_own / agy_conversation_id_own, plus resumable: true. Guarantees the next resume hits tier-1 (race-free) instead of the mtime-based disk-scan fallback. |
--reason <reason> |
Records stop_reason (default manual_stop). Convention: user_request / api_error / timeout / crash / manual_stop. |
--graceful |
tmux send-keys exit (/exit for claude, Exit for agy) → 3 s wait → if alive tmux kill-session (SIGTERM) → 5 s → kill -9 pane pid as last resort. Avoids hard-killing a TUI mid-write. |
Idempotency: in STOP mode, if the row is already status: stopped, the script
prints already stopped (...) and exits 0 — re-running is a safe no-op.
Backward compatibility: with none of these options, the base stop command behaves exactly as
before (hard→terminated, soft→archived).
State machine
running ──(stop --mode hard)────────────────► terminated
running ──(stop --capture-id/--reason/--graceful)► stopped (resumable, conv preserved)
running ──(stop --mode soft)────────────────► archived (tmux left alive)
stopped ──(stop --capture-id … again)───────► stopped (idempotent no-op)
any ──(stop --purge-conversation --yes)─► (conv deleted, resumable:false)
Fields written in STOP mode: status: stopped, stopped_at, stopped_at_epoch,
stop_reason, termination_mode: stop|graceful, and (with --capture-id)
claude_session_id_own/agy_conversation_id_own + resumable: true.
The script:
- Verifies the session is in agent-sessions.yaml
- If
delegate_job_idis set, automatically publishes aprogress --detail "terminating"event to the tmux-agent-orchestrate-delegate-job registry - Captures the
last_visible_statusfromtmux capture-pane(so we have a final TUI snapshot for audit) - For
hardmode:tmux kill-session -t <name>(which auto-SIGTERMs children including the agent) - For
purge-conversation: deletes~/.claude/projects/.../jsonl(claude) or~/.gemini/antigravity-cli/conversations/...db+brain/...(agy) - Updates the YAML entry
- If
delegate_job_idis set, publishes acompletedevent to the tmux-agent-orchestrate-delegate-job registry - Updates the YAML entry:
- name: <SESSION_NAME> status: terminated terminated_at: 2026-06-17T...Z terminated_at_epoch: ... # all original fields preserved
Pitfalls
tmux kill-sessiondoesn't just kill the session — it sends SIGHUP to the pane's child processes too. This is usually what you want (the agent process dies, no zombie reparenting to init). But if you wanted to keep the agent running outside tmux for some reason, usesoftmode.- Don't delete on-disk artifacts by default — the agent's
*.jsonl/conversations/*.dbis the data thattmux-agent-orchestrate-resumeneeds.--purge-conversationis for when the user is genuinely done with the conversation and wants zero recovery chance. - YAML is append-only until you write a stop — if a previous run left the entry as
runningbut tmux is actually dead (crash, host reboot), the YAML is stale. Runningtmux-agent-orchestrate-stop --mode hardwill detect "tmux already dead, just update YAML" and proceed. - Don't delete the
claude_session_id_own: nullplaceholder — when the user creates a fresh session withtmux-agent-orchestrate-createand never sent a message, the entry hasclaude_session_id_own: null. Stopping must preserve that field (it's the audit trail showing "this tmux session never produced a session id of its own"). - Monitor skill may still be tracking — if
tmux-agent-orchestrate-monitoris running a heartbeat loop, stopping a session while it watches will trigger itstmux ls != yamlreconciliation. That's expected — let the monitor run, it will mark the entry asterminatedon its own. Don't fight it.
Verification
# 1. tmux gone
tmux has-session -t "$SESSION_NAME" 2>/dev/null && echo "STILL ALIVE" || echo "OK: tmux gone"
# 2. YAML has terminated entry
python3 -c "
import yaml
d = yaml.safe_load(open('$AGENT_SESSIONS_YAML'))
s = [x for x in d['tmux_sessions'] if x['name']=='$SESSION_NAME'][0]
assert s['status'] == 'terminated', f'expected terminated, got {s[\"status\"]}'
assert s.get('terminated_at'), 'missing terminated_at'
print(f'OK: terminated at {s[\"terminated_at\"]}')
print(f' preserved: pane.pid={s[\"pane\"][\"pid\"]}, cmd={s[\"pane\"][\"cmd\"]}, cwd={s[\"pane\"][\"cwd\"]}')
"
# 3. (if --purge-conversation) disk artifacts gone (CLAUDE_PROJECT_DIR env var overrides default $HOME/.claude/projects)
[ -f "${CLAUDE_PROJECT_DIR:-$HOME/.claude/projects}/<projkey>/<uuid>.jsonl" ] && echo "WARN: jsonl still exists" || echo "OK: jsonl purged"
When NOT to use this skill
- Just detaching →
tmux detach(Ctrl-B d) or just close the terminal. The tmux session keeps running. - Stopping the agent inside but keeping tmux → send
Ctrl-Cor/exit(claude) /Ctrl-D(agy) viatmux send-keys. The tmux session stays but the agent process is gone; you can thentmux-agent-orchestrate-createagain to spawn a fresh agent in the same tmux session. - Replacing an existing session with a new one →
tmux-agent-orchestrate-stop --mode hardfirst, thentmux-agent-orchestrate-create.