start.sh
#!/bin/bash
# start.sh — Platform Workspace Launcher
# Запуск: bash start.sh  или  ./start.sh

cd "$WORKSPACE"

# ─── утилиты ──────────────────────────────────────────────────────────────────

hr() { echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"; }

# Сохраняем .claude.json в persistent volume перед запуском claude
save_claude_state() {
    [ -f "$HOME/.claude.json" ] && cp "$HOME/.claude.json" "$HOME/.claude/.claude.json.saved"
}

# ─── главное меню ─────────────────────────────────────────────────────────────

show_main() {
    clear
    hr
    echo "   Platform Workspace — $(hostname)  [$(date '+%Y-%m-%d %H:%M')]"
    hr
    echo ""
    echo "   0  →  Resume       (прерванные сессии)"
    echo "   1  →  ▲ Архитектор  (стандарты, методология, теория)"
    echo "   2  →  ◆ Проектор    (проекты, планирование, декомпозиция)"
    echo "   3  →  ● Операция    (серверы, деплой, баги, данные)"
    echo "   4  →  ✏  Кодирование (пиши код, реализуй, рефакторинг)"
    echo ""
    echo "   9  →  Восстановить Claude (credentials, настройки)"
    echo ""
    hr
    echo ""
    read -p "   Выбор: " choice
    echo ""

    case "$choice" in
        0) menu_resume ;;
        1) save_claude_state; cd "$WORKSPACE" && exec claude "режим архитектор" ;;
        2) menu_projector ;;
        3) save_claude_state; cd "$WORKSPACE" && exec claude "режим операция" ;;
        4) save_claude_state; cd "$WORKSPACE" && exec claude "режим кодирование" ;;
        9) menu_restore ;;
        *) save_claude_state; cd "$WORKSPACE" && exec claude "прочитай CLAUDE.md" ;;
    esac
}

# ─── 0. Resume ────────────────────────────────────────────────────────────────

menu_resume() {
    clear
    hr
    echo "   Resume — прерванные сессии"
    hr
    echo ""

    SESSIONS_DIR="$HOME/.claude/projects"
    if [ ! -d "$SESSIONS_DIR" ]; then
        echo "   Сессий нет."
        echo ""
        read -p "   Enter — назад: " _
        show_main
        return
    fi

    mapfile -t SESSION_FILES < <(
        find "$SESSIONS_DIR" -name "*.jsonl" \
            -not -path "*/subagents/*" \
            -not -path "*/tool-results/*" \
            2>/dev/null \
        | xargs ls -t 2>/dev/null \
        | head -10
    )

    if [ ${#SESSION_FILES[@]} -eq 0 ]; then
        echo "   Сессий нет."
        echo ""
        read -p "   Enter — назад: " _
        show_main
        return
    fi

    # Пишем python-скрипт во временный файл — избегаем heredoc в process substitution
    _PY=$(mktemp /tmp/session_status_XXXX.py)
    cat > "$_PY" << 'PYEOF'
import sys, json, re

pat_status = re.compile(r'^(?:[A-Z]+\s*\|\s*)?\d{4}-\d{2}-\d{2} \| ')
skip_prefixes = ('# ', 'Modify Claude', 'Use this', 'When ', 'Available', 'The following', '[Request', '<', '━')

status_lines = []
first_user = None

try:
    with open(sys.argv[1], 'r', errors='replace') as fh:
        for line in fh:
            try:
                d = json.loads(line)
                msg = d.get('message', {})
                role = msg.get('role', '')
                for c in msg.get('content', []):
                    if not isinstance(c, dict) or c.get('type') != 'text':
                        continue
                    text = c['text'].strip()
                    if role == 'assistant':
                        for l in text.split('\n'):
                            l = l.strip()
                            if pat_status.match(l):
                                status_lines.append(l)
                    elif role == 'user' and first_user is None:
                        if not any(text.startswith(p) for p in skip_prefixes) and len(text) > 3:
                            first_user = text[:100].replace('\n', ' ')
            except:
                pass
except:
    pass

if status_lines:
    print(status_lines[-1])
if first_user:
    print(f"  ↳ {first_user}")
PYEOF

    i=1
    declare -a SESSION_IDS
    declare -a SESSION_DIRS
    for f in "${SESSION_FILES[@]}"; do
        id="$(basename "$f" .jsonl)"
        ts="$(stat -c '%y' "$f" 2>/dev/null | cut -d'.' -f1)"

        mapfile -t STATUS_LINES < <(python3 "$_PY" "$f" 2>/dev/null)

        echo "   $i  [$ts]  $id"
        if [ ${#STATUS_LINES[@]} -gt 0 ]; then
            for sl in "${STATUS_LINES[@]}"; do
                echo "      $sl"
            done
        else
            echo "      (нет статусных строк)"
        fi
        echo ""
        SESSION_IDS+=("$id")
        SESSION_DIRS+=("$(dirname "$f")")
        i=$((i+1))
    done

    rm -f "$_PY"
    hr
    echo ""
    read -p "   Выбор [Enter = назад]: " pick
    echo ""

    if [ -z "$pick" ]; then
        show_main
        return
    fi

    idx=$((pick-1))
    if [ -n "${SESSION_IDS[$idx]:-}" ]; then
        save_claude_state
        cd "$WORKSPACE" && exec claude --resume "${SESSION_IDS[$idx]}"
    else
        echo "   Неверный выбор."
        sleep 1
        menu_resume
    fi
}

# ─── 2. Проектор ──────────────────────────────────────────────────────────────

menu_projector() {
    clear
    hr
    echo "   Проектор — проекты"
    hr
    echo ""

    # Собираем проекты: ищем index.yaml с type: org|biz|it|domain
    mapfile -t PROJECT_DIRS < <(
        find "$WORKSPACE/projects" -name "index.yaml" -not -path "*/archive/*" 2>/dev/null \
        | xargs grep -l "type:.*\(org\|biz\|it\|domain\)" 2>/dev/null \
        | sed 's|/index.yaml||' \
        | xargs ls -dt 2>/dev/null \
        | head -10
    )

    if [ ${#PROJECT_DIRS[@]} -eq 0 ]; then
        echo "   Проектов нет."
    else
        i=1
        declare -a PROJ_NAMES
        for d in "${PROJECT_DIRS[@]}"; do
            name="$(basename "$d")"
            echo "   $i  →  $name"
            PROJ_NAMES+=("$name")
            i=$((i+1))
        done
    fi

    echo ""
    hr
    echo ""
    read -p "   Выбор [Enter = новый проект]: " pick
    echo ""

    if [ -z "$pick" ]; then
        cd "$WORKSPACE" && exec claude "режим проектор"
    fi

    idx=$((pick-1))
    if [ -n "${PROJ_NAMES[$idx]:-}" ]; then
        cd "$WORKSPACE" && exec claude "режим проектор, проект ${PROJ_NAMES[$idx]}"
    else
        echo "   Неверный выбор."
        sleep 1
        menu_projector
    fi
}

# ─── 9. Восстановление ────────────────────────────────────────────────────────

menu_restore() {
    clear
    hr
    echo "   Восстановление Claude"
    hr
    echo ""

    SECRETS=/run/secrets

    # settings.json из воркспейса
    echo -n "   settings.json    ... "
    cp "$WORKSPACE/.claude/settings.json" "$HOME/.claude/settings.json" 2>/dev/null \
        && echo "OK" || echo "нет в workspace"

    # output-styles из воркспейса
    echo -n "   output-styles/   ... "
    mkdir -p "$HOME/.claude/output-styles"
    cp -r "$WORKSPACE/.claude/output-styles/." "$HOME/.claude/output-styles/" 2>/dev/null \
        && echo "OK" || echo "нет в workspace"

    # .credentials.json — только если отсутствует
    echo -n "   credentials.json ... "
    if [ -f "$HOME/.claude/.credentials.json" ]; then
        echo "уже есть"
    elif [ -f "$SECRETS/credentials.json" ]; then
        mkdir -p "$HOME/.claude"
        cp "$SECRETS/credentials.json" "$HOME/.claude/.credentials.json"
        chmod 600 "$HOME/.claude/.credentials.json"
        echo "OK (из secrets)"
    else
        echo "НЕТ (нужен claude login)"
    fi

    # .claude.json — только если отсутствует, приоритет volume > secrets
    echo -n "   .claude.json     ... "
    if [ -f "$HOME/.claude.json" ]; then
        echo "уже есть"
    elif [ -f "$HOME/.claude/.claude.json.saved" ]; then
        cp "$HOME/.claude/.claude.json.saved" "$HOME/.claude.json"
        echo "OK (из volume)"
    elif [ -f "$SECRETS/claude.json" ]; then
        cp "$SECRETS/claude.json" "$HOME/.claude.json"
        echo "OK (из secrets)"
    else
        echo '{"firstStartTime":"'"$(date -u +%Y-%m-%dT%H:%M:%S.000Z)"'"}' > "$HOME/.claude.json"
        echo "создан новый"
    fi

    echo ""
    hr
    echo ""
    read -p "   Enter — назад в меню: " _
    show_main
}

# ─── старт ────────────────────────────────────────────────────────────────────

show_main