add launch-assistant.sh: cc-ci-assistant — remote-control, non-loop helper
A general-purpose Claude session sharing the orchestrator's workspace + access, under remote-control (cc-ci-assistant), NOT on a loop. Sits idle until the orchestrator/operator hands it a plan/task, does it, reports, waits. Modelled on launch-orchestrator.sh: persistent pinned session-id (resume across relaunch), root-aware --dangerously-skip-permissions handling, start/fresh/status/attach/stop. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
102
cc-ci-plan/launch-assistant.sh
Executable file
102
cc-ci-plan/launch-assistant.sh
Executable file
@ -0,0 +1,102 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# launch-assistant.sh — start/resume the cc-ci ASSISTANT session in tmux under remote-control.
|
||||
#
|
||||
# The Assistant is a general-purpose, remote-controllable Claude session that shares the cc-ci
|
||||
# workspace + access with the orchestrator, but is NOT on a loop. It sits idle until the orchestrator
|
||||
# (or the operator) hands it a plan/task; it does the task against the workspace, reports back, and
|
||||
# waits for the next one. Modelled on launch-orchestrator.sh.
|
||||
#
|
||||
# Naming: tmux session AND remote-control name are both "cc-ci-assistant" (matching
|
||||
# cc-ci-orch / cc-ci-builder / cc-ci-adv / cc-ci-watchdog).
|
||||
#
|
||||
# Usage:
|
||||
# ./launch-assistant.sh start # resume the persistent assistant session (DEFAULT); creates it on first run
|
||||
# ./launch-assistant.sh fresh # start a NEW assistant session (new id)
|
||||
# ./launch-assistant.sh status # show tmux + remote-control state
|
||||
# ./launch-assistant.sh attach # tmux attach (Ctrl-b d to detach)
|
||||
# ./launch-assistant.sh stop # kill the tmux session (conversation persists on disk)
|
||||
set -euo pipefail
|
||||
|
||||
SESSION="${ASSISTANT_SESSION:-cc-ci-assistant}" # tmux session name == remote-control name
|
||||
WORKDIR="${ASSISTANT_DIR:-/srv/cc-ci}" # same workspace as the orchestrator
|
||||
CLAUDE_BIN="${CLAUDE_BIN:-/home/loops/.local/bin/claude}"
|
||||
# --dangerously-skip-permissions is blocked for root (use the env var there); as a non-root user the
|
||||
# flag works. Mirror launch.sh's detection.
|
||||
if [ "$(id -u)" = "0" ]; then export CLAUDE_DANGEROUSLY_SKIP_PERMISSIONS=1; CLAUDE_FLAGS="${CLAUDE_FLAGS:-}";
|
||||
else CLAUDE_FLAGS="${CLAUDE_FLAGS:---dangerously-skip-permissions}"; fi
|
||||
REMOTE_CONTROL="${REMOTE_CONTROL:-1}" # 1 => --remote-control (viewable at claude.ai/code)
|
||||
LOG_DIR="${LOG_DIR:-/srv/cc-ci/.cc-ci-logs}"
|
||||
ID_FILE="${ASSISTANT_ID_FILE:-$LOG_DIR/.assistant-session-id}"
|
||||
# Startup brief: defines the assistant's role. Injected as the session's first/next turn. No single quotes.
|
||||
STARTUP_PROMPT="${ASSISTANT_STARTUP_PROMPT-You are the cc-ci ASSISTANT — a general-purpose helper sharing the cc-ci workspace (/srv/cc-ci) and access (ssh cc-ci, .testenv, the plan files) with the orchestrator. You are NOT on a loop and you do NOT supervise the Builder/Adversary loops. Sit idle until the orchestrator or operator hands you a specific plan or task; then do it carefully, report the result, and wait for the next one. Respect single-writer discipline: the loops own the cc-ci product-repo clones (/srv/cc-ci/cc-ci, /srv/cc-ci/cc-ci-adv) — do not edit those unless a task explicitly says to. If you have just (re)launched and have no pending task, briefly confirm you are online and idle, then wait.}"
|
||||
|
||||
log() { printf '[assistant %(%H:%M:%S)T] %s\n' -1 "$*"; }
|
||||
die() { log "ERROR: $*"; exit 1; }
|
||||
session_alive() { tmux has-session -t "$SESSION" 2>/dev/null; }
|
||||
|
||||
preflight() {
|
||||
command -v tmux >/dev/null 2>&1 || die "missing dependency: tmux"
|
||||
command -v "$CLAUDE_BIN" >/dev/null 2>&1 || die "claude CLI not found at $CLAUDE_BIN (set CLAUDE_BIN)"
|
||||
[[ -d "$WORKDIR" ]] || die "workdir not found: $WORKDIR"
|
||||
mkdir -p "$LOG_DIR"
|
||||
# seed a stable session id on first ever launch
|
||||
[[ -f "$ID_FILE" ]] || cat /proc/sys/kernel/random/uuid > "$ID_FILE"
|
||||
}
|
||||
|
||||
sid() { cat "$ID_FILE" 2>/dev/null; }
|
||||
# does a transcript already exist for this id? (project dir derived from WORKDIR, e.g. -srv-cc-ci)
|
||||
have_transcript() {
|
||||
local key; key="$(printf '%s' "$WORKDIR" | sed 's#/#-#g')"
|
||||
[[ -f "$HOME/.claude/projects/$key/$(sid).jsonl" ]]
|
||||
}
|
||||
|
||||
# $1 = resume|fresh
|
||||
start() {
|
||||
local mode="${1:-resume}"
|
||||
preflight
|
||||
if session_alive; then
|
||||
log "$SESSION already running — leaving it (use '$0 stop' first to relaunch)"; return 0
|
||||
fi
|
||||
local rc="" sess="" id; id="$(sid)"
|
||||
[[ "$REMOTE_CONTROL" == "1" ]] && rc="--remote-control '$SESSION'"
|
||||
if [[ "$mode" == "fresh" ]]; then
|
||||
id="$(cat /proc/sys/kernel/random/uuid)"; echo "$id" > "$ID_FILE"
|
||||
sess="--session-id '$id'"; log "starting $SESSION FRESH (new id=$id)"
|
||||
elif have_transcript; then
|
||||
sess="--resume '$id'"; log "starting $SESSION (resume id=$id)"
|
||||
else
|
||||
sess="--session-id '$id'"; log "starting $SESSION (first run, pin id=$id)"
|
||||
fi
|
||||
local prompt_arg=""
|
||||
[[ -n "$STARTUP_PROMPT" ]] && prompt_arg="'$STARTUP_PROMPT'"
|
||||
tmux new-session -d -s "$SESSION" -c "$WORKDIR" \
|
||||
"$CLAUDE_BIN $sess $rc $CLAUDE_FLAGS $prompt_arg"
|
||||
tmux pipe-pane -o -t "$SESSION" "cat >> '$LOG_DIR/$SESSION.log'"
|
||||
log "started. status: $0 status | attach: tmux attach -t $SESSION | id: $id"
|
||||
}
|
||||
|
||||
case "${1:-start}" in
|
||||
start) start resume ;;
|
||||
fresh) start fresh ;;
|
||||
stop) if session_alive; then log "killing $SESSION"; tmux kill-session -t "$SESSION" || true; else log "$SESSION not running"; fi ;;
|
||||
status)
|
||||
if session_alive; then log "$SESSION: RUNNING"; ps -eo pid,etime,args | grep "[r]emote-control $SESSION" || true
|
||||
else log "$SESSION: stopped"; fi
|
||||
log "session id: $(sid) (file: $ID_FILE)" ;;
|
||||
attach) exec tmux attach -t "$SESSION" ;;
|
||||
*)
|
||||
cat <<EOF
|
||||
cc-ci assistant launcher — a remote-controllable, NON-loop helper sharing the orchestrator's workspace.
|
||||
|
||||
$0 start resume (or first-run create) the assistant session in tmux + remote-control (default)
|
||||
$0 fresh start a NEW assistant session (new id)
|
||||
$0 status show tmux + remote-control state and the session id
|
||||
$0 attach tmux attach to the session
|
||||
$0 stop kill the tmux session (conversation persists on disk)
|
||||
|
||||
Env: SESSION=$SESSION WORKDIR=$WORKDIR REMOTE_CONTROL=$REMOTE_CONTROL CLAUDE_BIN=$CLAUDE_BIN
|
||||
The orchestrator hands it plans/tasks; it does them, reports, and waits. Not on a loop.
|
||||
EOF
|
||||
;;
|
||||
esac
|
||||
Reference in New Issue
Block a user