Clean up all tmux occurrences from dev skills and integrate herdr wrapper translation shim with dynamic env and workspace ID parsing

This commit is contained in:
2026-07-19 16:57:00 +09:00
parent 42b54d7643
commit 30e606b0fa
20 changed files with 480 additions and 396 deletions
@@ -4,18 +4,18 @@
# bash create_session.sh --workspace <path> --agent <claude|agy> --role <role> [--session <name>] [--wrapper]
#
# 동작:
# 1) preflight: tmux/claude/agy 가용성, workspace 존재
# 2) tmux 세션 이름 결정 (--session 없으면 자동)
# 3) tmux 세션 시작 (claude 는 wrapper 우선, agy 는 인라인)
# 1) preflight: herdr/claude/agy 가용성, workspace 존재
# 2) herdr 세션 이름 결정 (--session 없으면 자동)
# 3) herdr 세션 시작 (claude 는 wrapper 우선, agy 는 인라인)
# 4) pane 메타 캡처 (pid, cmd, cwd)
# 5) agent-sessions.yaml 에 tmux_sessions[] 엔트리 append
# 5) agent-sessions.yaml 에 herdr_sessions[] 엔트리 append
# 6) 검증 출력
#
# Exit codes:
# 0 = success
# 1 = preflight failure
# 2 = invalid args
# 3 = tmux session already exists (use multi-agent-mux-resume or delete first)
# 3 = herdr session already exists (use multi-agent-mux-resume or delete first)
# 4 = agent-sessions.yaml append failure
set -euo pipefail
@@ -29,10 +29,10 @@ Options:
--workspace PATH project directory (required)
--agent AGENT claude | agy | hermes | cline (required)
--role ROLE assigned role (required)
--session NAME tmux session name (default: derived from workspace)
--session NAME herdr session name (default: derived from workspace)
--wrapper force use of ~/.local/bin/<session> wrapper even if not present
--dry-run print commands without executing
--tmux-server NAME specify isolated tmux server name
--herdr-server NAME specify isolated herdr server name
--submit-job PROMPT submit a job to multi-agent-mux-delegate-job registry with the given prompt
--onboard automatically submit a project alignment/orientation job to the new agent
--no-isolate disable state isolation (shares global configuration/history)
@@ -47,7 +47,7 @@ ROLE=""
SESSION_NAME=""
USE_WRAPPER=0
DRY_RUN=0
TMUX_SERVER_OPT=""
HERDR_SERVER_OPT=""
SUBMIT_JOB_PROMPT=""
ONBOARD=0
ISOLATE=1
@@ -60,7 +60,7 @@ while [ $# -gt 0 ]; do
--session) SESSION_NAME="$2"; shift 2 ;;
--wrapper) USE_WRAPPER=1; shift ;;
--dry-run) DRY_RUN=1; shift ;;
--tmux-server) TMUX_SERVER_OPT="$2"; shift 2 ;;
--herdr-server) HERDR_SERVER_OPT="$2"; shift 2 ;;
--submit-job) SUBMIT_JOB_PROMPT="$2"; shift 2 ;;
--onboard) ONBOARD=1; shift ;;
--isolate) ISOLATE=1; shift ;; # legacy compatibility
@@ -70,8 +70,8 @@ while [ $# -gt 0 ]; do
esac
done
if [ -n "$TMUX_SERVER_OPT" ]; then
export TMUX_SERVER_NAME="$TMUX_SERVER_OPT"
if [ -n "$HERDR_SERVER_OPT" ]; then
export HERDR_SERVER_NAME="$HERDR_SERVER_OPT"
fi
# Preflight
@@ -114,8 +114,8 @@ if [ -z "$SESSION_NAME" ]; then
fi
# 이미 살아있으면 실패
if _tmux has-session -t "$SESSION_NAME" 2>/dev/null; then
echo "ERROR: tmux session '$SESSION_NAME' already exists. Use multi-agent-mux-resume to attach, or multi-agent-mux-stop first." >&2
if _herdr has-session -t "$SESSION_NAME" 2>/dev/null; then
echo "ERROR: herdr session '$SESSION_NAME' already exists. Use multi-agent-mux-resume to attach, or multi-agent-mux-stop first." >&2
exit 3
fi
@@ -139,7 +139,7 @@ if [ "$ISOLATE" = "1" ]; then
fi
fi
# tmux 세션 띄우기
# herdr 세션 띄우기
LOCAL_BIN="${LOCAL_BIN:-$HOME/.local/bin}"
WRAPPER="$LOCAL_BIN/$SESSION_NAME"
@@ -152,7 +152,7 @@ if [ -n "$ISOLATION_ROOT" ]; then
ISO_CMD_ARGS="$(isolation_cmd_args "$AGENT" "$ISOLATION_ROOT")"
fi
# Resolve absolute path of the agent command to prevent tmux PATH inheritance issues (especially on macOS)
# Resolve absolute path of the agent command to prevent herdr PATH inheritance issues (especially on macOS)
RESOLVED_BIN="$AGENT"
if [ "$AGENT" = "cline" ]; then
if command -v cline >/dev/null 2>&1; then
@@ -184,29 +184,29 @@ spawn() {
nohup "$WRAPPER" >/dev/null 2>&1 &
disown
else
_tmux new-session -d -s "$SESSION_NAME" -x 140 -y 40 -c "$WORKSPACE" "$CMD_FULL"
_herdr new-session -d -s "$SESSION_NAME" -x 140 -y 40 -c "$WORKSPACE" "$CMD_FULL"
fi
;;
agy|hermes|cline)
_tmux new-session -d -s "$SESSION_NAME" -x 140 -y 40 -c "$WORKSPACE" "$CMD_FULL"
_herdr new-session -d -s "$SESSION_NAME" -x 140 -y 40 -c "$WORKSPACE" "$CMD_FULL"
;;
*) echo "ERROR: --agent must be claude, agy, hermes or cline, got: $AGENT" >&2; exit 2 ;;
esac
}
if [ "$DRY_RUN" = "1" ]; then
echo "[dry-run] would spawn: tmux session '$SESSION_NAME' in $WORKSPACE (agent=$AGENT)"
echo "[dry-run] would spawn: herdr session '$SESSION_NAME' in $WORKSPACE (agent=$AGENT)"
exit 0
fi
spawn
# Trap for rolling back/cleaning up tmux session if script exits due to error
cleanup_tmux_on_error() {
# Trap for rolling back/cleaning up herdr session if script exits due to error
cleanup_herdr_on_error() {
local exit_code=$?
if [ $exit_code -ne 0 ]; then
echo "⚠️ Error occurred during initialization. Rolling back and killing tmux session '$SESSION_NAME'..." >&2
_tmux kill-session -t "$SESSION_NAME" 2>/dev/null || true
echo "⚠️ Error occurred during initialization. Rolling back and killing herdr session '$SESSION_NAME'..." >&2
_herdr kill-session -t "$SESSION_NAME" 2>/dev/null || true
# T3 rollback: 이 세션용으로 프로비저닝한 격리 홈 제거 (경로 가드 후 rm)
if [ -n "$ISOLATION_ROOT" ] && [ -d "$ISOLATION_ROOT" ]; then
case "$ISOLATION_ROOT" in
@@ -215,7 +215,7 @@ cleanup_tmux_on_error() {
fi
fi
}
trap cleanup_tmux_on_error EXIT
trap cleanup_herdr_on_error EXIT
# TUI 준비 대기
if ! wait_for_tui_ready "$SESSION_NAME" "$AGENT"; then
@@ -224,14 +224,14 @@ if ! wait_for_tui_ready "$SESSION_NAME" "$AGENT"; then
fi
# pane 메타 캡처
PANE_PID=$(_tmux list-panes -t "$SESSION_NAME" -F '#{pane_pid}' 2>/dev/null || echo "")
PANE_CWD=$(_tmux list-panes -t "$SESSION_NAME" -F '#{pane_current_path}' 2>/dev/null || echo "$WORKSPACE")
PANE_CMD=$(_tmux list-panes -t "$SESSION_NAME" -F '#{pane_current_command}' 2>/dev/null || echo "$AGENT")
TMUX_EPOCH=$(date +%s)
PANE_PID=$(_herdr list-panes -t "$SESSION_NAME" -F '#{pane_pid}' 2>/dev/null || echo "")
PANE_CWD=$(_herdr list-panes -t "$SESSION_NAME" -F '#{pane_current_path}' 2>/dev/null || echo "$WORKSPACE")
PANE_CMD=$(_herdr list-panes -t "$SESSION_NAME" -F '#{pane_current_command}' 2>/dev/null || echo "$AGENT")
HERDR_EPOCH=$(date +%s)
NOW_ISO=$(date -u +'%Y-%m-%dT%H:%M:%SZ')
# 시작 명령 (CMD_FULL 은 spawn 전에 isolation 디스패치를 반영해 확정됨 — T4)
START_CMD="herdr agent start \"$SESSION_NAME\" --workspace \"${TMUX_SERVER_NAME:-default}\" --cwd \"$WORKSPACE\" -- $CMD_FULL"
START_CMD="herdr agent start \"$SESSION_NAME\" --workspace \"${HERDR_SERVER_NAME:-default}\" --cwd \"$WORKSPACE\" -- $CMD_FULL"
# If --onboard is specified, automatically build the onboarding prompt
if [ "$ONBOARD" = "1" ] && [ -z "$SUBMIT_JOB_PROMPT" ]; then
@@ -255,14 +255,14 @@ if [ -n "$SUBMIT_JOB_PROMPT" ]; then
else
delegate_agent="antigravity-cli"
fi
agent_session="tmux:$SESSION_NAME"
agent_session="herdr:$SESSION_NAME"
DELEGATE_JOB_ID=$(delegate_submit_job "$SUBMIT_JOB_PROMPT" "$delegate_agent" "$agent_session")
echo "Submitted delegated job: $DELEGATE_JOB_ID"
fi
if [ ! -f "$AGENT_SESSIONS_YAML" ]; then
mkdir -p "$(dirname "$AGENT_SESSIONS_YAML")"
echo "tmux_sessions: []" > "$AGENT_SESSIONS_YAML"
echo "herdr_sessions: []" > "$AGENT_SESSIONS_YAML"
fi
# atomic_dump_yaml: flock + temp+rename + .bak + schema validate (P0-B).
@@ -276,9 +276,9 @@ fi
atomic_dump_yaml "$AGENT_SESSIONS_YAML" \
SESSION_NAME="$SESSION_NAME" AGENT="$AGENT" NOW_ISO="$NOW_ISO" \
TMUX_EPOCH="$TMUX_EPOCH" PANE_PID="$PANE_PID" PANE_CWD="$PANE_CWD" \
HERDR_EPOCH="$HERDR_EPOCH" PANE_PID="$PANE_PID" PANE_CWD="$PANE_CWD" \
CMD_FULL="$CMD_FULL" START_CMD="$START_CMD" CHILD_PID="$CHILD_PID" \
TMUX_SERVER_NAME="${TMUX_SERVER_NAME:-default}" \
HERDR_SERVER_NAME="${HERDR_SERVER_NAME:-default}" \
DELEGATE_JOB_ID="$DELEGATE_JOB_ID" ROLE="$ROLE" \
ISOLATION_UUID="$ISOLATION_UUID" ISOLATION_ROOT="$ISOLATION_ROOT" \
ISOLATION_LEVER="$ISOLATION_LEVER" ISOLATION_SEEDED="$ISOLATION_SEEDED" <<'PYEOF'
@@ -286,11 +286,11 @@ name = os.environ['SESSION_NAME']
agent = os.environ['AGENT']
role = os.environ['ROLE']
pid = os.environ.get('PANE_PID', '')
epoch = os.environ.get('TMUX_EPOCH', '')
server_name = os.environ.get('TMUX_SERVER_NAME', 'default')
epoch = os.environ.get('HERDR_EPOCH', '')
server_name = os.environ.get('HERDR_SERVER_NAME', 'default')
server_opt = f"-L {server_name} " if server_name and server_name != 'default' else ""
sessions = d.setdefault('tmux_sessions', [])
sessions = d.setdefault('herdr_sessions', [])
# P0-D: 같은 이름 엔트리가 status=running 이면만 거부. terminated/archived 는
# 재사용 가능 — 낡은 엔트리를 제거하고 새로 append (create -> delete -> create).
@@ -304,9 +304,9 @@ entry = {
'name': name,
'status': 'running',
'role': role,
'tmux_session_created_at': os.environ['NOW_ISO'],
'tmux_session_epoch': int(epoch) if epoch.isdigit() else 0,
'tmux_server': server_name,
'herdr_session_created_at': os.environ['NOW_ISO'],
'herdr_session_epoch': int(epoch) if epoch.isdigit() else 0,
'herdr_server': server_name,
'delegate_job_id': os.environ.get('DELEGATE_JOB_ID', '') or None,
'pane': {
'index': 0,
@@ -373,7 +373,7 @@ PYEOF
echo
echo "=== created ==="
echo "tmux session: $SESSION_NAME (pane pid $PANE_PID, cmd $PANE_CMD, cwd $PANE_CWD)"
echo "herdr session: $SESSION_NAME (pane pid $PANE_PID, cmd $PANE_CMD, cwd $PANE_CWD)"
if [ -n "$DELEGATE_JOB_ID" ]; then
echo "delegate job: $DELEGATE_JOB_ID"
@@ -389,7 +389,7 @@ On failure run: $pub --event error --detail '<one-line reason>'.
Task: $SUBMIT_JOB_PROMPT"
# Inject instructions into the tmux pane
# Inject instructions into the herdr pane
rc=0
inject_instructions "$SESSION_NAME" "$instructions" "$DELEGATE_JOB_ID" || rc=$?
if [ "$rc" -ne 0 ]; then