Files
multi-agent-mux/skills/multi-agent-status/scripts/status.sh
T
Godopu 8a3abff2d6 initial: canary multi-agent skills with tmux isolation support
- lib.sh: TMUX_SERVER_NAME env var, _tmux helper, shim externalized
  to TMPDIR with recursive guard, resolve_tmux_server helper for
  YAML-driven server routing
- multi-agent-create: --tmux-server opt-in flag, YAML tmux_server
  field for orphan prevention
- multi-agent-delete/resume/status/agent-sessions-monitor: use
  resolve_tmux_server to auto-route to correct isolated server
- SKILL.md × 4: documented isolation server workflow
- Verified by claude review (R1+re-run) + agy R2 patches
  (orphan prevention + shim location fix)
2026-06-19 13:32:36 +00:00

89 lines
3.2 KiB
Bash
Executable File

#!/usr/bin/env bash
# status.sh — multi-agent-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)/agent-sessions-monitor/scripts/reconcile.sh"
JSON=0
[ "${1:-}" = "--json" ] && JSON=1
[ -f "$AGENT_SESSIONS_YAML" ] || { echo "ERROR: $AGENT_SESSIONS_YAML not found. Run multi-agent-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
DRIFT_JSON="$DRIFT_JSON" env_python "$AGENT_SESSIONS_YAML" <<'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 '?'
sessions = d.get('tmux_sessions', [])
print(f"agent-sessions status — {drift['timestamp']} (tmux_confirmed={drift['tmux_confirmed']})")
print("=" * 116)
print(f"{'NAME':<44} {'SERVER':<12} {'YAML':<10} {'TMUX':<6} {'CMD':<6} {'RESUME':<8} DRIFT")
print("-" * 116)
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)
drs = ','.join(drift_by_name.get(name, [])) or '-'
print(f"{name:<44} {server:<12} {status:<10} {tmux:<6} {cmd:<6} {res:<8} {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("-" * 116)
for dr in extra:
print(f" [{dr['class']}] {dr['msg']}")
print("=" * 116)
print(f"alive tmux: {sorted(alive)}")
PYEOF