refactor(skills): rename multi-agent-* + agent-sessions-monitor + delegate-job to tmux-agent-orchestrate-*

Renamed 6 skills directories to tmux-agent-orchestrate-* prefix:
- multi-agent-create → tmux-agent-orchestrate-create
- multi-agent-resume → tmux-agent-orchestrate-resume
- multi-agent-delete → tmux-agent-orchestrate-delete
- multi-agent-status → tmux-agent-orchestrate-status
- agent-sessions-monitor → tmux-agent-orchestrate-monitor
- delegate-job → tmux-agent-orchestrate-delegate-job

Updated:
- skills/lib.sh internal paths (delegate_submit_job etc.)
- skills/tmux-agent-orchestrate-status/scripts/status.sh (monitor path)
- skills/tmux-agent-orchestrate-monitor/scripts/reconcile.sh
- .gitignore (HTML ignore patterns)
- 6 SKILL.md frontmatter (name, related_skills, prereq_skills) and body
- All script headers and Korean comments

Notes:
- tmux session naming convention unchanged (<slug>-creator-<agent>) — workspace identifier based, kept for backward compatibility
- Existing 2 sessions in -L multi-agent-canary untouched
- YAML delegate_job_id / agent-session (tmux:canary-...) preserved for log history compatibility

Verified on isolated server -L agy-rename-test (kill-server after).
This commit is contained in:
2026-06-19 23:27:27 +00:00
parent 4fa276f3c5
commit e9fc763d31
25 changed files with 146 additions and 146 deletions
@@ -0,0 +1,124 @@
---
name: tmux-agent-orchestrate-status
description: "Read-only instant snapshot of all agent tmux sessions — name, YAML status, tmux alive, pane cmd/cwd, resume UUID on disk, and any drift. No Kanban, no mutation. Reuses reconcile.sh --dry-run for the diff logic. Use when you want to know 'what's running RIGHT NOW' without spinning up a Kanban monitor worker."
version: 1.0.0
author: godopu
license: MIT
platforms: [linux, macos]
environments: [terminal, tmux]
metadata:
hermes:
tags: [agent, tmux, claude, antigravity, agy, status, read-only, snapshot]
related_skills: [tmux-agent-orchestrate-create, tmux-agent-orchestrate-resume, tmux-agent-orchestrate-delete, tmux-agent-orchestrate-monitor]
prereq_skills: [tmux-agent-orchestrate-create, tmux-agent-orchestrate-monitor]
---
# Multi-Agent Status — Read-Only Instant Snapshot
> **Companion skills**: `tmux-agent-orchestrate-create` (start), `tmux-agent-orchestrate-resume` (re-attach), `tmux-agent-orchestrate-delete` (terminate), `tmux-agent-orchestrate-monitor` (live polling).
> **Tmux Isolation**: `status` 명령은 YAML에 등록된 모든 세션의 격리 서버(`tmux_server` 필드)를 자동으로 조회하여 상태를 확인하므로, `TMUX_SERVER_NAME` 환경변수를 수동으로 지정하지 않아도 모든 격리 서버의 세션 상태를 통합 조회합니다.
> **Single source of truth**: `~/PuKi/lab/agent_sessions/agent-sessions.yaml`.
## What this skill does
Print a single table of every agent tmux session, comparing YAML state to actual tmux state. **No mutation. No Kanban. No polling loop.**
This is the "what's running right now?" answer — faster than dispatching `tmux-agent-orchestrate-monitor` (which polls every 30s) and safer than `reconcile.sh --once --emit-diff` (which mutates as a side effect).
## Pre-flight
```bash
command -v tmux
command -v python3
test -f ~/PuKi/lab/agent_sessions/agent-sessions.yaml
```
If `agent-sessions.yaml` doesn't exist or is malformed → print clear error, exit 1. **Do not create it.** (Use `tmux-agent-orchestrate-create` first.)
## Workflow
```bash
bash ~/PuKi/lab/agent_sessions/skills/tmux-agent-orchestrate-status/scripts/status.sh [--json]
```
The script:
1. Calls `reconcile.sh --once --emit-diff --dry-run` (read-only; no YAML mutation) for the drift snapshot
2. Loads `agent-sessions.yaml` (read-only) to enrich the table
3. For each row in `tmux_sessions[]`:
- tmux alive? (via `tmux has-session -t <name>`)
- pane cmd, cwd (via `tmux list-panes`)
- resume UUID on disk? (claude: `~/.claude/projects/<key>/<uuid>.jsonl`; agy: `~/.gemini/antigravity-cli/conversations/<uuid>.db`)
4. For each tmux session matching `*-creator-*` not in YAML → flag as "unregistered"
5. Prints a table (default) or JSON (with `--json`)
## Output format (default = aligned table)
```
agent-sessions status — 2026-06-19T14:20:00Z (tmux_confirmed=True)
========================================================================================================================================
NAME SERVER YAML TMUX CMD RESUME JOB_ID JOB_STATUS DRIFT
----------------------------------------------------------------------------------------------------------------------------------------
lab-landing-page-creator-claude default running alive claude yes - - -
lab-landing-page-creator-agy default terminated dead agy yes 5fe09ba8 completed -
lab-paper-pdf2md-creator-claude default running alive claude scan - - -
========================================================================================================================================
```
## Output format (`--json`)
```json
{
"yaml_path": "...",
"tmux_sessions_alive": ["..."],
"yaml_entries": [...],
"rows": [
{
"name": "lab-landing-page-creator-claude",
"yaml_status": "running",
"tmux_alive": true,
"pane_cmd": "claude",
"pane_cwd": "/home/.../refer_landing_page",
"resume_uuid_on_disk": true,
"drift": null
},
{
"name": "lab-landing-page-creator-agy",
"yaml_status": "terminated",
"tmux_alive": false,
"drift": "yaml-says-terminated-but-disk-uuid-still-present"
}
],
"unregistered": [],
"drifts": []
}
```
## Drift classes (read-only — never mutates)
| Class | Detection | Meaning |
|---|---|---|
| `A` | YAML `running`, tmux dead | session died without going through `tmux-agent-orchestrate-delete`. *Could* auto-terminate but won't — that's `tmux-agent-orchestrate-monitor`'s job. |
| `B` | tmux alive, not in YAML | ad-hoc session someone started without `tmux-agent-orchestrate-create`. Suggest: "use tmux-agent-orchestrate-create to register, or tmux kill-session to clean up." |
| `C` | YAML has `claude_session_id_own: null` AND a new *.jsonl exists | new session id materialized; suggest: "run tmux-agent-orchestrate-resume or reconcile to register it." |
| `D` | YAML has UUID in `agent_identities`, but the on-disk artifact is gone | stale UUID; user should `tmux-agent-orchestrate-delete --purge-conversation` to clean up. |
## Pitfalls
- **Do NOT use this skill to drive mutations** — the output is a snapshot, not a call to action. If you need to fix drifts, dispatch `tmux-agent-orchestrate-monitor` (Kanban worker) or run `tmux-agent-orchestrate-resume` / `tmux-agent-orchestrate-delete` manually.
- **Read-only is enforced by script** — `status.sh` opens the YAML with `open(path)` (no `'w'`), never calls `tmux kill-session`, never writes anywhere. The `reconcile.sh --dry-run` mode is the same path.
- **If `agent-sessions.yaml` is malformed** — print the YAML error verbatim and exit 1. Do NOT attempt recovery (that's `tmux-agent-orchestrate-delete --purge-conversation` or manual edit's job).
- **Sessions outside the `<workspace>-creator-*` naming convention** are still shown but tagged `ad-hoc` — they didn't go through `tmux-agent-orchestrate-create` and aren't tracked in YAML.
## When to use
- "Is the claude session still running?" → this skill, not the monitor
- "What UUID does this workspace have?" → this skill
- "Is there drift between YAML and reality?" → this skill, then dispatch monitor or fix manually
- Quick sanity check before dispatching a long Kanban task
## When NOT to use
- Continuous live tracking → `tmux-agent-orchestrate-monitor` (Kanban worker)
- Recovering from corruption → manual edit + `.bak` restore
- Polling more than once a minute → `tmux-agent-orchestrate-monitor` (it dedupes)
+118
View File
@@ -0,0 +1,118 @@
#!/usr/bin/env bash
# status.sh — tmux-agent-orchestrate-status 의 부속 스크립트 (READ-ONLY)
# 한 번 호출로 현재 agent 세션 상태표를 출력. 부수효과 없음.
# reconcile.sh --dry-run 을 재사용해 drift 를 계산하고 (P1-E), YAML/디스크에서
# 보강한 표를 그린다. YAML 을 절대 수정하지 않는다.
#
# Usage: bash status.sh [--json]
set -euo pipefail
source "$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)/lib.sh"
RECONCILE="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)/tmux-agent-orchestrate-monitor/scripts/reconcile.sh"
JSON=0
[ "${1:-}" = "--json" ] && JSON=1
[ -f "$AGENT_SESSIONS_YAML" ] || { echo "ERROR: $AGENT_SESSIONS_YAML not found. Run tmux-agent-orchestrate-create first." >&2; exit 1; }
# read-only drift snapshot — reconcile.sh --dry-run (no side effects)
DRIFT_JSON="$(bash "$RECONCILE" --once --emit-diff --dry-run)"
if [ "$JSON" = "1" ]; then
printf '%s\n' "$DRIFT_JSON"
exit 0
fi
# Project root (parent of skills/) holds the tmux-agent-orchestrate-delegate-job .hermes registry.
# Resolved relative to this script — no hardcoded absolute path (review item 6).
PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)"
DRIFT_JSON="$DRIFT_JSON" env_python "$AGENT_SESSIONS_YAML" PROJECT_ROOT="$PROJECT_ROOT" <<'PYEOF'
import os, json, glob
import yaml
yaml_path = os.environ['YAML_PATH']
home = os.environ['HOME_DIR']
drift = json.loads(os.environ['DRIFT_JSON'])
with open(yaml_path) as f:
d = yaml.safe_load(f) or {}
alive = set(drift.get('tmux_sessions_alive', []))
drift_by_name = {}
for dr in drift.get('drifts', []):
drift_by_name.setdefault(dr['name'], []).append(dr['class'])
def resume_on_disk(s):
# workspace-SCOPED check only — per-row own id, never a global identity (P0-C)
name = s.get('name', '')
cwd = (s.get('pane') or {}).get('cwd', '')
if name.endswith('-creator-claude'):
u = s.get('claude_session_id_own')
if u:
key = cwd.replace('/', '-').replace('_', '-')
return 'yes' if os.path.exists(f"{home}/.claude/projects/{key}/{u}.jsonl") else 'MISSING'
key = cwd.replace('/', '-').replace('_', '-')
return 'scan' if glob.glob(f"{home}/.claude/projects/{key}/*.jsonl") else 'no'
if name.endswith('-creator-agy'):
u = s.get('agy_conversation_id_own')
if u:
return 'yes' if os.path.exists(f"{home}/.gemini/antigravity-cli/conversations/{u}.db") else 'MISSING'
return 'no'
return '?'
def get_job_status(s):
jid = s.get('delegate_job_id')
if not jid:
return ('-', '-')
project_root = os.environ.get('PROJECT_ROOT', '.')
# Candidate locations (review item 6: project-root-relative, no hardcoded abs paths):
# 1) cwd-relative registry 2) project-root registry 3) project-root audit log
candidates = [
os.path.join('.hermes', 'jobs', f"{jid}.json"),
os.path.join(project_root, '.hermes', 'jobs', f"{jid}.json"),
os.path.join(project_root, '.hermes', 'delegate_job_logs', jid, 'status.json'),
]
for path in candidates:
if os.path.exists(path):
try:
with open(path) as jf:
job_data = json.load(jf)
return (jid, job_data.get('status', 'unknown'))
except Exception:
pass
return (jid, 'unknown')
sessions = d.get('tmux_sessions', [])
print(f"agent-sessions status — {drift['timestamp']} (tmux_confirmed={drift['tmux_confirmed']})")
print("=" * 136)
print(f"{'NAME':<44} {'SERVER':<12} {'YAML':<10} {'TMUX':<6} {'CMD':<6} {'RESUME':<8} {'JOB_ID':<10} {'JOB_STATUS':<12} DRIFT")
print("-" * 136)
if not sessions:
print("(no sessions registered)")
for s in sessions:
name = s.get('name', '?')
server = s.get('tmux_server') or 'default'
status = s.get('status', '?')
tmux = 'alive' if f"{name}|{server}" in alive else 'dead'
cmd = (s.get('pane') or {}).get('cmd', '?')
res = resume_on_disk(s)
jid, jstatus = get_job_status(s)
drs = ','.join(drift_by_name.get(name, [])) or '-'
print(f"{name:<44} {server:<12} {status:<10} {tmux:<6} {cmd:<6} {res:<8} {jid:<10} {jstatus:<12} {drs}")
# drifts not tied to a registered row (e.g. class B unregistered, class D cache)
known = {s.get('name') for s in sessions}
extra = [dr for dr in drift.get('drifts', []) if dr['name'] not in known]
if extra:
print("-" * 136)
for dr in extra:
print(f" [{dr['class']}] {dr['msg']}")
print("=" * 136)
print(f"alive tmux: {sorted(alive)}")
PYEOF