New package packages/seat and wrapper scripts/mosaic. `launch <seat>` writes <dataRoot>/seats/<layout>/<seat>/registration.json and then execs the seat's launch.sh unchanged; `seat task <seat> <text>` edits the task only. The board reads registrations, matches by sessions directory, and lets a registered task, project or workspace override the derived value with a source tag. The four repository launch scripts register themselves unless already registered or run with --check. Fleet launchers untouched; one-liner on the plan page. Review found the record path keyed by seat name alone (repo and fleet "darkwing" would collide); fixed by keying on layout. Also: the Pi pin refusal now names installed and required versions. Tests: seat 15, control-board 89, launch scripts 5, registry 69, config 24. Co-Authored-By: Claude Fable 5.1 <[email protected]>
88 lines
3.7 KiB
Bash
Executable File
88 lines
3.7 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Rocko's native Claude Code development session.
|
|
set -euo pipefail
|
|
REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
# Register this seat with the control board (packages/seat) unless already
|
|
# registered by `mosaic launch` or only running the checks.
|
|
if [ -z "${MOSAIC_LAUNCH_REGISTERED:-}" ] && ! printf '%s\n' "$@" | grep -qx -- '--check'; then
|
|
exec "$REPO/scripts/mosaic" launch --repo "$REPO" --harness claude-code rocko -- "$@"
|
|
fi
|
|
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[@]}"
|