#!/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