Files
multi-agent-mux/skills/tmux-agent-orchestrate-stop/SKILL.md
T
Godopu 50b2b201b8 refactor(skills): rename tmux-agent-orchestrate-delete -> stop (step 1)
User decision: 2-step approach (Step 1 = simple rename, Step 2 = option
redefinition in a separate round).

Changes (mechanical, history preserved):
- skills/tmux-agent-orchestrate-delete/ -> skills/tmux-agent-orchestrate-stop/ (git mv)
- scripts/delete_session.sh -> scripts/stop_session.sh (git mv)
- sed s/orchestrate-delete/orchestrate-stop/g + delete_session.sh->stop_session.sh
  across 7 files (0 residual of either pattern)
- SKILL.md frontmatter 'name' -> tmux-agent-orchestrate-stop
- related_skills / companion refs in create/status/monitor/resume SKILL.md updated

NOT in this commit (deferred to step 2):
- Option redefinition (--purge-conversation, --mode soft clarification)
- Deprecation shim (external consumers = 0, no need)

6-route surface preserved (create/resume/stop/status/monitor + delegate-job).

Verified on isolated server -L claude-rename-step1-test (kill-server after):
- syntax PASS (all .sh + py_compile)
- E2E via renamed stop_session.sh: capture-id records id + status=stopped,
  status.sh renders it (DRIFT=-), idempotency exit 0
- 0 stale 'tmux-agent-orchestrate-delete' / 'delete_session' references
- git history preserved (rename detected as R)
- Global skill untouched; real YAML + main canary -L multi-agent-canary untouched

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 15:48:27 +00:00

9.7 KiB

name, description, version, author, license, platforms, environments, metadata
name description version author license platforms environments metadata
tmux-agent-orchestrate-stop Terminate an agent tmux session (claude, antigravity/agy) and update .hermes/agent-sessions.yaml to mark it terminated with timestamp. Does NOT delete on-disk conversation artifacts (jsonl/db) — those are preserved for future resume. Use when ending a work session, switching to a different one, or cleaning up before a fresh start. 1.0.0 godopu MIT
linux
macos
terminal
tmux
hermes
tags related_skills prereq_skills
agent
tmux
claude
antigravity
agy
multi-agent
delete
terminate
cleanup
tmux-agent-orchestrate-create
tmux-agent-orchestrate-resume
tmux-agent-orchestrate-monitor
tmux-agent-orchestrate-create
tmux-agent-orchestrate-resume

Multi-Agent Delete — Terminate 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: delete 명령은 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 as terminated. Preserves:

  • The tmux session's recorded pane.pid / cmd / cwd / mcp_attachments for audit
  • The agent's on-disk conversation (claude *.jsonl, agy conversations/*.db) — so the user can tmux-agent-orchestrate-resume later
  • The start_command so a future tmux-agent-orchestrate-create --session <name> reproduces the same tmux spec

The user explicitly chooses:

  • soft delete (default): update YAML only; leave tmux running. Useful when "delete" really means "I'm done with this card".
  • hard delete: 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 delete (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 delete (YAML only — tmux left running)
bash skills/tmux-agent-orchestrate-stop/scripts/stop_session.sh \
  --session "$SESSION_NAME" --mode soft

# 2. hard delete (default — kill tmux + update YAML)
bash skills/tmux-agent-orchestrate-stop/scripts/stop_session.sh \
  --session "$SESSION_NAME" --mode hard

# 3. hard delete + 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, delete 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, delete behaves exactly as before (hardterminated, softarchived).

State machine

running ──(delete --mode hard)────────────────► terminated
running ──(delete --capture-id/--reason/--graceful)► stopped   (resumable, conv preserved)
running ──(delete --mode soft)────────────────► archived      (tmux left alive)
stopped ──(delete --capture-id … again)───────► stopped       (idempotent no-op)
any     ──(delete --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:

  1. Verifies the session is in agent-sessions.yaml
  2. If delegate_job_id is set, automatically publishes a progress --detail "terminating" event to the tmux-agent-orchestrate-delegate-job registry
  3. Captures the last_visible_status from tmux capture-pane (so we have a final TUI snapshot for audit)
  4. For hard mode: tmux kill-session -t <name> (which auto-SIGTERMs children including the agent)
  5. For purge-conversation: deletes ~/.claude/projects/.../jsonl (claude) or ~/.gemini/antigravity-cli/conversations/...db + brain/... (agy)
  6. Updates the YAML entry
  7. If delegate_job_id is set, publishes a completed event to the tmux-agent-orchestrate-delegate-job registry
  8. 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-session doesn'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, use soft mode.
  • Don't delete on-disk artifacts by default — the agent's *.jsonl / conversations/*.db is the data that tmux-agent-orchestrate-resume needs. --purge-conversation is for when the user is genuinely done with the conversation and wants zero recovery chance.
  • YAML is append-only until you write a delete — if a previous run left the entry as running but tmux is actually dead (crash, host reboot), the YAML is stale. Running tmux-agent-orchestrate-stop --mode hard will detect "tmux already dead, just update YAML" and proceed.
  • Don't delete the claude_session_id_own: null placeholder — when the user creates a fresh session with tmux-agent-orchestrate-create and never sent a message, the entry has claude_session_id_own: null. Deletion 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-monitor is running a heartbeat loop, deleting a session while it watches will trigger its tmux ls != yaml reconciliation. That's expected — let the monitor run, it will mark the entry as terminated on 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 detachingtmux detach (Ctrl-B d) or just close the terminal. The tmux session keeps running.
  • Stopping the agent inside but keeping tmux → send Ctrl-C or /exit (claude) / Ctrl-D (agy) via tmux send-keys. The tmux session stays but the agent process is gone; you can then tmux-agent-orchestrate-create again to spawn a fresh agent in the same tmux session.
  • Replacing an existing session with a new onetmux-agent-orchestrate-stop --mode hard first, then tmux-agent-orchestrate-create.