#!/usr/bin/env bash # Rocko's native Claude Code development session. set -euo pipefail REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" cd "$REPO" fail() { echo "rocko: $*" >&2; exit 1; } usage() { echo 'Usage: agents/rocko/launch.sh [--fresh] [--check] [--soul FILE] [--constitution FILE] [--user FILE]' } FRESH=false CHECK=false SOUL="$REPO/agents/rocko/SOUL.md" CONSTITUTION="$REPO/contracts/CONSTITUTION.md" USER_FILE="" while [ "$#" -gt 0 ]; do case "$1" in --fresh) FRESH=true; shift ;; --check) CHECK=true; shift ;; --soul|--constitution|--user) [ "$#" -ge 2 ] && [ -n "$2" ] || fail "$1 needs a file" case "$1" in --soul) SOUL="$2" ;; --constitution) CONSTITUTION="$2" ;; --user) USER_FILE="$2" ;; esac shift 2 ;; --help|-h) usage; exit 0 ;; *) usage >&2; fail "unknown argument: $1" ;; esac done source "$REPO/scripts/common.sh" load_config USER_FILE="${USER_FILE:-$MOSAIC_DATA_ROOT/user/USER.md}" command -v claude >/dev/null || fail 'Claude Code is required on PATH' command -v node >/dev/null || fail 'Node.js is required' command -v flock >/dev/null || fail 'flock is required' VERSION="$(claude --version)" CONTEXT_FILES=("$CONSTITUTION" "$REPO/contracts/STANDARDS.md" "$SOUL" "$USER_FILE" "$REPO/AGENTS.md" "$REPO/agents/rocko/CONTEXT.md") for file in "${CONTEXT_FILES[@]}"; do [ -f "$file" ] && [ -r "$file" ] && [ -s "$file" ] || fail "missing, unreadable or empty context: $file" done STATE="$REPO/.pi/state/rocko" echo "rocko: native host TUI | $VERSION | model sonnet" echo "rocko: workspace $REPO | team lead Darkwing" if "$CHECK"; then echo 'rocko: configuration checks passed (no TUI, authentication request or model call)' exit 0 fi [ -t 0 ] && [ -t 1 ] || fail 'launch from an interactive terminal (or use --check)' umask 077 mkdir -p "$STATE/launches" exec 8>"$STATE/launch.lock" flock -n 8 || fail 'another rocko TUI from this launcher is active' SESSION_FILE="$STATE/session-id" if ! "$FRESH" && [ -e "$SESSION_FILE" ]; then [ -f "$SESSION_FILE" ] && [ -r "$SESSION_FILE" ] || fail 'invalid session-id file; inspect it or use --fresh' SESSION_ID="$(cat "$SESSION_FILE")" [[ "$SESSION_ID" =~ ^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$ ]] || fail 'invalid session-id; inspect it or use --fresh' SESSION_ARGS=(--resume "$SESSION_ID") echo "rocko: resuming conversation $SESSION_ID" else SESSION_ID="$(node -e 'console.log(require("node:crypto").randomUUID())')" SESSION_ARGS=(--session-id "$SESSION_ID") echo "rocko: starting conversation $SESSION_ID; earlier conversations are preserved" fi LAUNCH="$(mktemp -d "$STATE/launches/launch.XXXXXXXX")" PROMPT="$LAUNCH/context.md" for file in "${CONTEXT_FILES[@]}"; do printf '\n===== %s (%s) =====\n' "$(basename "$file")" "$file" >> "$PROMPT" cat "$file" >> "$PROMPT" printf '\n' >> "$PROMPT" done sha256sum "$PROMPT" > "$LAUNCH/context.sha256" printf '%s\n' "$SESSION_ID" > "$LAUNCH/session-id" # Retain each prior ID in its launch receipt; replace only the current pointer. printf '%s\n' "$SESSION_ID" > "$STATE/session-id.tmp" mv "$STATE/session-id.tmp" "$SESSION_FILE" echo "rocko: context snapshot $PROMPT" unset MOSAIC_LAUNCH_INCARNATION export MOSAIC_AGENT_NAME=rocko exec claude --model sonnet --name Rocko \ --append-system-prompt "$(cat "$PROMPT")" "${SESSION_ARGS[@]}"