pi-system/bin/SubAgenten
Raimund Bauer fb3daab33f feat/init: PiSystem Infrastruktur-Repo mit SubConfirm
Enthält alle Pi-Orchestrator-Infrastrukturkomponenten:
- bin/Sub* Skripte (SubAgenten, SubStatus, SubWatcher, SubConfirm)
- extensions/ (arbeitsweise-guard, confirm-deletion, etc.)
- memory/ (arbeitsweise, subagent-autocheck)
- agent/AGENTS.md mit SubConfirm-Reaktionslogik
- install.sh: deterministisches, idempotentes Setup für neue Maschinen

SubConfirm (neu): Stasis-Detektor der alle 30s tmux-Sessions prüft.
Bei unverändertem Output sendet er den vollständigen Pane-Inhalt
an die Alert-Datei — der Orchestrator beurteilt selbst ob Handlung nötig.
Kein Keyword-Matching.
2026-06-02 11:53:37 +02:00

201 lines
No EOL
7.6 KiB
Bash
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/bin/bash
# SubAgenten — Starte einen Subagenten mit Pi in einem isolierten Verzeichnis
#
# Nutzung: SubAgenten <aufgaben-name> <aufgaben-beschreibung> [MODELL_CMD]
#
# MODELL_CMD (optional): Welches Pi-Kommando starten (Default: GlmPi)
# Verfügbare Kommandos: TurboPi, MiniPi, GlmPi, DeepPi, FlashPi, GPTPi, GeminiPi
#
# Beispiel:
# SubAgenten "pi-sticks" "Pi auf stick2-5 installieren laut Runbook"
# SubAgenten "debug" "Debug Auth-Problem" TurboPi
# SubAgenten "quick-task" "Kurze Aufgabe" MiniPi
#
# Funktionsweise:
# 1. Isoliertes Verzeichnis in ~/.pi/subagents/<name>/ anlegen (persistent, kein /tmp)
# 2. tmux-Session starten (unsichtbar), Pi darin ausführen
# 3. Warten bis Pi bereit ist, dann /name + Aufgabe per tmux send-keys senden
# 4. gnome-terminal-Fenster öffnen und an tmux-Session attach-en
# 5. Du siehst das Fenster, Pi arbeitet — du kannst jederzeit reinschreiben
#
# Bei Rückfragen des Subagenten:
# - Das Fenster ist direkt sichtbar — einfach Antwort eintippen
# - Oder: tmux attach -t <session-name> (z.B. in einem zweiten Terminal)
# - Verlassen ohne Session zu beenden: Ctrl+B, dann D
#
# Umgebung:
# SUBAGENT_SILENT=1 — Keine Ausgabe, nur tmux-Session-Name auf stdout
set -euo pipefail
NAME="${1:-}"
TASK="${2:-}"
MODEL_CMD="${3:-GlmPi}" # Default: GlmPi (GLM 5.1)
if [ -z "$NAME" ] || [ -z "$TASK" ]; then
echo "Fehler: Name und Aufgabe erforderlich."
echo ""
echo "Nutzung: SubAgenten <aufgaben-name> <aufgaben-beschreibung> [MODELL_CMD]"
echo ""
echo "MODELL_CMD (optional): TurboPi, MiniPi, GlmPi, DeepPi, FlashPi, GPTPi, GeminiPi (Default: GlmPi)"
echo ""
echo "Beispiele:"
echo ' SubAgenten "pi-sticks" "Pi auf stick2-5 installieren"'
echo ' SubAgenten "debug" "Debug Auth-Problem" TurboPi'
exit 1
fi
# Prüfe ob das angegebene Kommando existiert
if ! command -v "$MODEL_CMD" &> /dev/null; then
echo "Warnung: '$MODEL_CMD' nicht gefunden. Verwende GlmPi als Fallback."
MODEL_CMD="GlmPi"
fi
# Slug aus dem Namen für tmux-Session + Arbeitsverzeichnis
SESSION_SLUG=$(echo "$NAME" | tr '[:upper:]' '[:lower:]' | tr -c 'a-z0-9' '-')
WORKDIR="${SUBAGENT_BASE_DIR:-$HOME/.pi/subagents}/${SESSION_SLUG}"
# Prüfen ob Session bereits existiert
if tmux has-session -t "$SESSION_SLUG" 2>/dev/null; then
echo "Fehler: tmux-Session '$SESSION_SLUG' existiert bereits."
echo " Zum Ansehen: tmux attach -t $SESSION_SLUG"
echo " Zum Beenden: tmux kill-session -t $SESSION_SLUG"
exit 1
fi
# Isoliertes Verzeichnis anlegen
mkdir -p "$WORKDIR"
if [ -z "${SUBAGENT_SILENT:-}" ]; then
echo ""
echo "╔══════════════════════════════════════════╗"
echo "║ 🚀 Subagent: $NAME"
echo "╚══════════════════════════════════════════╝"
echo ""
echo "📁 Arbeitsverzeichnis: $WORKDIR"
echo "🪟 Terminal-Fenster wird geöffnet..."
echo ""
fi
# tmux-Session im Hintergrund starten (ohne Fenster)
# PI_ORCHESTRATOR explizit entfernen — darf nicht an Subagenten vererbt werden,
# sonst blockiert der arbeitsweise-guard auch im Subagenten curl/wget/write.
# MODEL_CMD ist das Pi-Kommando (z.B. GlmPi, TurboPi, MiniPi)
tmux new-session -d -s "$SESSION_SLUG" -c "$WORKDIR" "unset PI_ORCHESTRATOR; ${MODEL_CMD}; echo '=== Pi beendet ==='; read"
if [ -z "${SUBAGENT_SILENT:-}" ]; then
echo "⏳ Warte auf Pi-Start..."
fi
# Warten bis Pi seinen Prompt anzeigt (max 30 Sekunden)
# Früherer Bugfix: Pattern auf Box-Drawing-Zeichen (╭─ ───) matchte nicht,
# weil Unicode-Zeichen in tmux-Capture anders kodiert sind.
# Stattdessen: Warten auf Pi-Prompt-Indikator (auto) oder Provider-Name
TIMEOUT=30
ELAPSED=0
while [ $ELAPSED -lt $TIMEOUT ]; do
# Prüfen ob tmux-Session noch lebt
if ! tmux has-session -t "$SESSION_SLUG" 2>/dev/null; then
echo "Fehler: tmux-Session '$SESSION_SLUG' ist abgestürzt."
exit 1
fi
# Prüfen ob Pi bereit ist — suche nach Prompt-Indikatoren:
# - (auto) = auto-modus aktiv
# - Provider-Kürzel wie (zai), (openrouter)
# - Arbeitsverzeichnis im Prompt
# -a = treat binary as text (tmux-capture liefert UTF-8 Bytes)
CAPTURE=$(tmux capture-pane -t "$SESSION_SLUG" -p 2>/dev/null | tail -3)
if echo "$CAPTURE" | grep -a -qE "\(auto\)|\(zai\)|\(openrouter\)" 2>/dev/null; then
break
fi
sleep 1
ELAPSED=$((ELAPSED + 1))
done
if [ $ELAPSED -ge $TIMEOUT ] && [ -z "${SUBAGENT_SILENT:-}" ]; then
echo "⚠️ Timeout beim Warten auf Pi-Start (${TIMEOUT}s), versuche trotzdem..."
fi
# Name setzen
tmux send-keys -t "$SESSION_SLUG" "/name ${SESSION_SLUG}" Enter
sleep 1
# Orchestrator-ID ermitteln
ORCHESTRATOR_ID=$(intercom list 2>/dev/null | grep -oP '\[self.*?\]\(([a-f0-9]+)' | grep -oP '[a-f0-9]+$' 2>/dev/null || echo "")
# Aufgabe senden
if [ -n "$ORCHESTRATOR_ID" ]; then
TASK_WITH_INTERCOM="${TASK}
INTERCOM:
Orchestrator-ID: $ORCHESTRATOR_ID
Nach Abschluss: intercom send an $ORCHESTRATOR_ID mit Ergebnis
Antwort nicht an \"orchestrator\" senden — das funktioniert nicht!"
tmux send-keys -t "$SESSION_SLUG" "$TASK_WITH_INTERCOM" Enter
else
tmux send-keys -t "$SESSION_SLUG" "AUFGABE: ${TASK}" Enter
fi
if [ -z "${SUBAGENT_SILENT:-}" ]; then
echo "✅ Name und Aufgabe gesendet."
echo ""
echo "📋 Fenster ist offen — du siehst alles live."
echo ""
echo " Bei Rückfragen:"
echo " - Direkt ins Fenster tippen (es hat den Fokus)"
echo " - Oder: tmux attach -t $SESSION_SLUG"
echo " - Verlassen: Ctrl+B, dann D (Session läuft weiter)"
echo ""
echo "🔑 Session: $SESSION_SLUG"
echo ""
fi
# Aktuelles Fenster merken (bevor gnome-terminal den Fokus stiehlt)
SAVED_WINDOW=$(xdotool getactivewindow 2>/dev/null || echo "")
# Fenster-Offset (20px pro SubAgent nach rechts, max 9 — Überlappung vermeiden)
SUBAGENT_COUNTER_FILE="/tmp/.subagent-geo-counter"
SUBAGENT_COUNT=$(cat "$SUBAGENT_COUNTER_FILE" 2>/dev/null || echo 0)
SUBAGENT_COUNT=$((SUBAGENT_COUNT + 1))
[ "$SUBAGENT_COUNT" -gt 9 ] && SUBAGENT_COUNT=1
echo "$SUBAGENT_COUNT" > "$SUBAGENT_COUNTER_FILE"
X_OFFSET=$((4652 + (SUBAGENT_COUNT - 1) * 20))
# gnome-terminal-Fenster öffnen und an tmux-Session attach-en
gnome-terminal \
--geometry=64x24+${X_OFFSET}+100 \
--title="Subagent: $NAME" \
--working-directory="$WORKDIR" \
-- bash -c "tmux attach-session -t '$SESSION_SLUG'; echo 'Fenster kann geschlossen werden.'; read"
# Attachment-Check: Session muss innerhalb von 5s sichtbar (attached) sein.
# Wenn gnome-terminal nicht geöffnet hat (kein DISPLAY, crash etc.), Session killen.
ATTACH_TIMEOUT=5
ATTACH_ELAPSED=0
while [ $ATTACH_ELAPSED -lt $ATTACH_TIMEOUT ]; do
if tmux ls -F '#{session_name}:#{session_attached}' 2>/dev/null | grep -q "^${SESSION_SLUG}:1$"; then
break
fi
sleep 1
ATTACH_ELAPSED=$((ATTACH_ELAPSED + 1))
done
if ! tmux ls -F '#{session_name}:#{session_attached}' 2>/dev/null | grep -q "^${SESSION_SLUG}:1$"; then
echo "FEHLER: Kein sichtbares Fenster für Session '${SESSION_SLUG}' nach ${ATTACH_TIMEOUT}s."
echo " Mögliche Ursache: DISPLAY nicht gesetzt oder gnome-terminal nicht verfügbar."
echo " Session wird beendet — keine unsichtbaren Hintergrundprozesse."
tmux kill-session -t "$SESSION_SLUG" 2>/dev/null
exit 1
fi
# Fokus zurück zum ursprünglichen Terminal holen
sleep 0.3
if [ -n "$SAVED_WINDOW" ]; then
xdotool windowactivate "$SAVED_WINDOW" 2>/dev/null || true
fi
if [ -z "${SUBAGENT_SILENT:-}" ]; then
echo "✨ Fertig."
fi