scripts/agent.sh --host-dev delegates to scripts/agent-host-dev.sh; agents/darkwing/launch.sh provides the native development TUI (context, skills, coding tools, /goal). Root SOUL.md is the M14-era default-collaborator persona captured by the ACT-1 context work. Launcher regression checks pass (test-darkwing-launch.mjs).
121 lines
5.2 KiB
Bash
Executable File
121 lines
5.2 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Host-development implementation for scripts/agent.sh --host-dev <name>.
|
|
set -euo pipefail
|
|
REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
cd "$REPO"
|
|
|
|
usage() {
|
|
echo 'Usage: scripts/agent.sh --host-dev AGENT [--fresh] [--check] [--soul FILE] [--constitution FILE] [--user FILE]'
|
|
echo 'Default: resume the latest agent development conversation; first launch starts one.'
|
|
}
|
|
fail() { echo "${AGENT:-tui}: $*" >&2; exit 1; }
|
|
case "${1:-}" in
|
|
--help|-h) usage; exit 0 ;;
|
|
"") usage >&2; exit 1 ;;
|
|
esac
|
|
AGENT="$1"
|
|
shift
|
|
[[ "$AGENT" =~ ^[a-z0-9][a-z0-9._-]{0,63}$ ]] || fail "invalid agent name"
|
|
[ -d "$REPO/agents/$AGENT" ] || fail "unknown agent: $AGENT"
|
|
SOUL="$REPO/agents/$AGENT/SOUL.md"
|
|
CONSTITUTION="$REPO/contracts/CONSTITUTION.md"
|
|
USER_FILE=""
|
|
FRESH=false
|
|
CHECK=false
|
|
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
|
|
|
|
# Reuse the deployment's validated provider/model and live user profile.
|
|
source "$REPO/scripts/common.sh"
|
|
load_config
|
|
[ "$MOSAIC_ADAPTER" = pi ] || fail 'native launcher requires the pi adapter'
|
|
USER_FILE="${USER_FILE:-$MOSAIC_DATA_ROOT/user/USER.md}"
|
|
PI="$REPO/node_modules/.bin/pi"
|
|
[ -x "$PI" ] || fail 'run npm ci --ignore-scripts --no-audit --no-fund in the repository first'
|
|
PIN="$(node -p "require('./package.json').dependencies['@earendil-works/pi-coding-agent']")"
|
|
[ "$("$PI" --version)" = "$PIN" ] || fail "local Pi must match package.json ($PIN); run npm ci"
|
|
command -v flock >/dev/null || fail 'flock is required'
|
|
CONTEXT_FILES=("$CONSTITUTION" "$REPO/contracts/STANDARDS.md" "$SOUL" "$USER_FILE" "$REPO/AGENTS.md" "$REPO/agents/$AGENT/CONTEXT.md")
|
|
for file in "${CONTEXT_FILES[@]}"; do
|
|
[ -f "$file" ] && [ -r "$file" ] && [ -s "$file" ] || fail "missing, unreadable or empty context: $file"
|
|
done
|
|
SKILLS=(ms-tools ms-file-read ms-file-write ms-conductor ms-communications
|
|
ms-agent-watch ms-sdlc ms-proactive-agent ms-goal ms-unslop)
|
|
SKILL_ARGS=()
|
|
for skill in "${SKILLS[@]}"; do
|
|
[ -s "$REPO/skills/$skill/SKILL.md" ] || fail "missing skill: $skill"
|
|
SKILL_ARGS+=(--skill "$REPO/skills/$skill")
|
|
done
|
|
[ -s "$REPO/extensions/goal/index.ts" ] || fail 'missing canonical goal extension'
|
|
STATE="$REPO/.pi/state/$AGENT"
|
|
echo "$AGENT: native host TUI | Pi $PIN | $MOSAIC_PROVIDER/$MOSAIC_MODEL"
|
|
echo "$AGENT: workspace $REPO"
|
|
echo "$AGENT: SOUL=$SOUL | CONSTITUTION=$CONSTITUTION | USER=$USER_FILE"
|
|
echo "$AGENT: sessions $STATE/sessions | skills: ${SKILLS[*]}"
|
|
if "$CHECK"; then
|
|
echo "$AGENT: 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/sessions" "$STATE/launches"
|
|
exec 8>"$STATE/launch.lock"
|
|
flock -n 8 || fail "another $AGENT TUI from this launcher is active"
|
|
|
|
# The native packaging path verifies source copies and refuses installation drift.
|
|
scripts/sync-dev-extensions.sh
|
|
SESSION_ARGS=(--session-dir "$STATE/sessions")
|
|
shopt -s nullglob dotglob
|
|
SESSION_FILES=("$STATE/sessions/"*.jsonl)
|
|
SESSION_ENTRIES=("$STATE/sessions/"*)
|
|
if ! "$FRESH"; then
|
|
if [ "${#SESSION_FILES[@]}" -gt 0 ]; then
|
|
# Pi --continue otherwise silently skips broken files and may start fresh.
|
|
node "$REPO/agents/$AGENT/validate-sessions.mjs" "${SESSION_FILES[@]}"
|
|
SESSION_ARGS+=(--continue)
|
|
echo "$AGENT: resuming latest development conversation with current launch context"
|
|
elif [ "${#SESSION_ENTRIES[@]}" -gt 0 ]; then
|
|
fail 'session directory has state but no JSONL conversation; inspect it before using --fresh'
|
|
else
|
|
echo "$AGENT: starting first development conversation"
|
|
fi
|
|
else
|
|
echo "$AGENT: starting a fresh conversation; earlier conversations are preserved"
|
|
fi
|
|
|
|
# A private snapshot per launch avoids the container loader's shared prompt file.
|
|
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"
|
|
echo "$AGENT: context snapshot $PROMPT"
|
|
unset MOSAIC_LAUNCH_INCARNATION
|
|
export MOSAIC_AGENT_NAME="$AGENT"
|
|
# Explicit resources only; preserve Pi's built-in coding prompt and tool guidance.
|
|
# goal_report must be in the tool allowlist as well as the extension being loaded.
|
|
exec "$PI" --approve --offline --no-context-files --no-extensions --no-skills \
|
|
--no-prompt-templates --no-themes \
|
|
--extension "$REPO/.pi/extensions/goal/index.ts" \
|
|
"${SKILL_ARGS[@]}" \
|
|
--tools read,bash,edit,write,grep,find,ls,goal_report \
|
|
--provider "$MOSAIC_PROVIDER" --model "$MOSAIC_MODEL" \
|
|
--append-system-prompt "$PROMPT" "${SESSION_ARGS[@]}"
|