484 lines
19 KiB
Bash
Executable File
484 lines
19 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
# Install the 14 dev_agent_team opencode agents AND the self-contained runtime
|
|
# tree into a local opencode config.
|
|
#
|
|
# The runtime tree is installed under $OPENCODE_DEV_AGENT_TEAM (default
|
|
# $XDG_CONFIG_HOME/opencode/dev-agent-team, fallback ~/.config/opencode/dev-agent-team):
|
|
# bin/ - memory-lifecycle.sh, repo-bootstrap.sh, verify-permission-patterns.sh, test suites
|
|
# skills/ - 12 general-purpose skills + SKILLS.md (managed, read-only)
|
|
# improvements/ - README (only if absent) + pending/applied/rejected (user data, never overwritten)
|
|
# install-manifest.json - version, date, installed file list + sha256
|
|
#
|
|
# Usage:
|
|
# ./scripts/install.sh # install runtime tree (agents + runtime)
|
|
# ./scripts/install.sh --uninstall # remove manifest-tracked install
|
|
# ./scripts/install.sh --uninstall --purge # also remove improvements/ user data
|
|
# ./scripts/install.sh --migrate # import pending improvement proposals (no-op today)
|
|
# ./scripts/install.sh --self-test # run the new runtime test suites in a temp HOME
|
|
#
|
|
# Override target with: OPENCODE_AGENTS_DIR=/some/dir
|
|
# Override runtime root with: OPENCODE_DEV_AGENT_TEAM=/some/runtime
|
|
|
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
TARGET="${OPENCODE_AGENTS_DIR:-$HOME/.config/opencode/agents}"
|
|
EXPECTED_COUNT=14
|
|
KEEP_BACKUPS=5
|
|
|
|
# Runtime root resolution (D3/D8): env var first, then XDG_CONFIG_HOME, then
|
|
# ~/.config/opencode/dev-agent-team.
|
|
if [ -n "${OPENCODE_DEV_AGENT_TEAM:-}" ]; then
|
|
RUNTIME_ROOT="$OPENCODE_DEV_AGENT_TEAM"
|
|
elif [ -n "${XDG_CONFIG_HOME:-}" ]; then
|
|
RUNTIME_ROOT="$XDG_CONFIG_HOME/opencode/dev-agent-team"
|
|
else
|
|
RUNTIME_ROOT="$HOME/.config/opencode/dev-agent-team"
|
|
fi
|
|
|
|
MANIFEST="$RUNTIME_ROOT/install-manifest.json"
|
|
STAMP="$(date +%Y%m%d_%H%M%S)_$$"
|
|
|
|
log() { echo "==> $*"; }
|
|
|
|
# --------------------------------------------------------------------- #
|
|
# Usage / flags
|
|
# --------------------------------------------------------------------- #
|
|
usage() {
|
|
sed -n '2,30p' "$0" | sed 's/^# \{0,1\}//'
|
|
exit 2
|
|
}
|
|
|
|
SELF_TEST=0
|
|
UNINSTALL=0
|
|
PURGE=0
|
|
MIGRATE=0
|
|
|
|
for arg in "$@"; do
|
|
case "$arg" in
|
|
--uninstall) UNINSTALL=1 ;;
|
|
--purge) PURGE=1 ;;
|
|
--migrate) MIGRATE=1 ;;
|
|
--self-test) SELF_TEST=1 ;;
|
|
-h|--help) usage ;;
|
|
*) echo "ERROR: unknown option: $arg" >&2; usage ;;
|
|
esac
|
|
done
|
|
|
|
# --------------------------------------------------------------------- #
|
|
# Uninstall (D9)
|
|
# --------------------------------------------------------------------- #
|
|
if [ "$UNINSTALL" = "1" ]; then
|
|
log "Uninstalling dev_agent_team agents + runtime"
|
|
|
|
# Honor the agents target recorded by the manifest if not overridden.
|
|
if [ -z "${OPENCODE_AGENTS_DIR:-}" ] && [ -f "$MANIFEST" ]; then
|
|
MAN_TARGET="$(sed -n 's/.*"agents_target": "\([^"]*\)".*/\1/p' "$MANIFEST" | head -1)"
|
|
[ -n "$MAN_TARGET" ] && TARGET="$MAN_TARGET"
|
|
fi
|
|
|
|
if [ -f "$MANIFEST" ]; then
|
|
# Remove manifest-tracked agent files from $TARGET (entries with file: agents/...)
|
|
removed=0
|
|
while IFS= read -r line; do
|
|
tgt="$(printf '%s' "$line" | sed -n 's/.*"target": "\([^"]*\)".*/\1/p')"
|
|
if [ -n "$tgt" ] && [ -f "$TARGET/$tgt" ]; then
|
|
rm -f "$TARGET/$tgt"
|
|
removed=$((removed + 1))
|
|
fi
|
|
done < <(grep '"file": "agents/' "$MANIFEST" || true)
|
|
log "Removed $removed agent file(s) tracked by manifest."
|
|
else
|
|
log "No manifest at $MANIFEST; removing runtime root directly."
|
|
fi
|
|
|
|
# Leave agent backups in place and print their location.
|
|
BACKUP_TARGET="$TARGET/.backup"
|
|
if [ -d "$BACKUP_TARGET" ]; then
|
|
log "Agent backups left in place under: $BACKUP_TARGET"
|
|
fi
|
|
|
|
# Remove runtime root; preserve improvements/ unless --purge.
|
|
if [ -d "$RUNTIME_ROOT" ]; then
|
|
if [ "$PURGE" = "1" ]; then
|
|
rm -rf "$RUNTIME_ROOT"
|
|
log "Removed runtime root: $RUNTIME_ROOT (--purge: including improvements/ user data)"
|
|
else
|
|
# Preserve improvements/ user data: move it aside to a location OUTSIDE
|
|
# the runtime root (never inside, or the rm -rf below would delete it),
|
|
# remove the rest of the tree, then restore. Clean up the temp save dir.
|
|
IMPROV="$RUNTIME_ROOT/improvements"
|
|
if [ -d "$IMPROV" ]; then
|
|
IMPROV_PARENT="$(dirname "$RUNTIME_ROOT")"
|
|
IMPROV_SAVE="$(mktemp -d "$IMPROV_PARENT/.improvements-user.XXXXXX" 2>/dev/null || true)"
|
|
if [ -z "$IMPROV_SAVE" ]; then
|
|
# Fail-safe: single sibling path (still outside the runtime root).
|
|
IMPROV_SAVE="$IMPROV_PARENT/.improvements-user"
|
|
rm -rf "$IMPROV_SAVE"
|
|
fi
|
|
mv "$IMPROV" "$IMPROV_SAVE/user"
|
|
rm -rf "$RUNTIME_ROOT"
|
|
mkdir -p "$RUNTIME_ROOT"
|
|
mv "$IMPROV_SAVE/user" "$IMPROV"
|
|
rmdir "$IMPROV_SAVE" >/dev/null 2>&1 || true
|
|
log "Preserved improvements/ user data at: $IMPROV"
|
|
log "Runtime root removed (excluding improvements/)."
|
|
else
|
|
rm -rf "$RUNTIME_ROOT"
|
|
log "Runtime root removed: $RUNTIME_ROOT"
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
# Ask before removing the rc export line (it may be shared).
|
|
RC_FILE=""
|
|
[ -f "$HOME/.bashrc" ] && RC_FILE="$HOME/.bashrc"
|
|
[ -z "$RC_FILE" ] && [ -f "$HOME/.profile" ] && RC_FILE="$HOME/.profile"
|
|
if [ -n "$RC_FILE" ] && grep -q 'OPENCODE_DEV_AGENT_TEAM' "$RC_FILE"; then
|
|
echo ""
|
|
echo "The shell-rc export of OPENCODE_DEV_AGENT_TEAM exists in $RC_FILE."
|
|
printf 'Remove it? [y/N] ' >&2
|
|
read -r answer || answer=""
|
|
case "$answer" in
|
|
y|Y|yes|YES) sed -i '/OPENCODE_DEV_AGENT_TEAM/d' "$RC_FILE"; log "Removed OPENCODE_DEV_AGENT_TEAM export from $RC_FILE." ;;
|
|
*) log "Left OPENCODE_DEV_AGENT_TEAM export in $RC_FILE." ;;
|
|
esac
|
|
fi
|
|
|
|
log "Uninstall complete. Never touched: project memory/, .opencode/, AgentsReport/, user opencode.json."
|
|
exit 0
|
|
fi
|
|
|
|
# --------------------------------------------------------------------- #
|
|
# Migrate (D10) — minimal one-time import of pending proposals (no-op today)
|
|
# --------------------------------------------------------------------- #
|
|
if [ "$MIGRATE" = "1" ]; then
|
|
log "Migrate mode: import pending improvement proposals from source checkout."
|
|
SRC_IMPROV="$ROOT/improvements"
|
|
DST_IMPROV="$RUNTIME_ROOT/improvements"
|
|
imported=0
|
|
if [ -d "$SRC_IMPROV/pending" ]; then
|
|
mkdir -p "$DST_IMPROV/pending" 2>/dev/null || true
|
|
for p in "$SRC_IMPROV"/pending/*.md; do
|
|
[ -f "$p" ] || continue
|
|
name="$(basename "$p")"
|
|
if [ ! -f "$DST_IMPROV/pending/$name" ]; then
|
|
cp -p "$p" "$DST_IMPROV/pending/$name"
|
|
log "Imported pending: $name"
|
|
imported=$((imported + 1))
|
|
else
|
|
log "Skipped (already present): $name"
|
|
fi
|
|
done
|
|
fi
|
|
log "Migrate complete: imported $imported pending proposal(s) (no-op today — only README exists)."
|
|
exit 0
|
|
fi
|
|
|
|
# --------------------------------------------------------------------- #
|
|
# Agent install (preserve existing behavior exactly)
|
|
# --------------------------------------------------------------------- #
|
|
log "dev_agent_team installer"
|
|
log "Source: $ROOT/agents"
|
|
log "Destination: $TARGET"
|
|
log "Runtime: $RUNTIME_ROOT"
|
|
|
|
shopt -s nullglob
|
|
AGENT_FILES=( "$ROOT"/agents/*.md )
|
|
shopt -u nullglob
|
|
|
|
# Gate BEFORE any copying: refuse to touch anything unless the full set exists.
|
|
if (( ${#AGENT_FILES[@]} < EXPECTED_COUNT )); then
|
|
echo "ERROR: expected $EXPECTED_COUNT agent files in $ROOT/agents, found ${#AGENT_FILES[@]}. Aborting." >&2
|
|
exit 1
|
|
fi
|
|
|
|
mkdir -p "$TARGET"
|
|
|
|
BACKUP_DIR="$TARGET/.backup/$STAMP"
|
|
|
|
# Back up any pre-existing same-named agent files before touching them.
|
|
backed_up=0
|
|
for src_path in "${AGENT_FILES[@]}"; do
|
|
name="$(basename "$src_path")"
|
|
if [[ -e "$TARGET/$name" ]]; then
|
|
mkdir -p "$BACKUP_DIR"
|
|
cp -p "$TARGET/$name" "$BACKUP_DIR/$name"
|
|
echo " backed up: $name -> .backup/$STAMP/$name"
|
|
backed_up=$((backed_up + 1))
|
|
fi
|
|
done
|
|
|
|
# Retention cap (agents) — keep only the most recent KEEP_BACKUPS stamp dirs.
|
|
if (( backed_up > 0 )); then
|
|
shopt -s nullglob
|
|
backup_dirs=( "$TARGET"/.backup/[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]_[0-9][0-9][0-9][0-9][0-9][0-9]_* )
|
|
shopt -u nullglob
|
|
excess=$(( ${#backup_dirs[@]} - KEEP_BACKUPS ))
|
|
if (( excess > 0 )); then
|
|
for prune_dir in "${backup_dirs[@]:0:excess}"; do
|
|
[ -d "$prune_dir" ] || continue
|
|
rm -rf "$prune_dir"
|
|
echo " pruned old backup: .backup/$(basename "$prune_dir")"
|
|
done
|
|
fi
|
|
fi
|
|
|
|
# Copy the agents.
|
|
copied=0
|
|
for src_path in "${AGENT_FILES[@]}"; do
|
|
name="$(basename "$src_path")"
|
|
cp -p "$src_path" "$TARGET/$name"
|
|
echo " copied: $name"
|
|
copied=$((copied + 1))
|
|
done
|
|
|
|
if (( copied != EXPECTED_COUNT )); then
|
|
echo "ERROR: expected $EXPECTED_COUNT agent files, but installed $copied. Aborting." >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Decision-4 gate 1 — byte integrity (cmp -s).
|
|
for src_path in "${AGENT_FILES[@]}"; do
|
|
name="$(basename "$src_path")"
|
|
if ! cmp -s "$src_path" "$TARGET/$name"; then
|
|
echo "ERROR: integrity check failed for '$name' ($TARGET/$name differs from $src_path). Aborting." >&2
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
# Decision-4 gate 2 — permission-engine semantics.
|
|
echo "==> Verifying permission-engine semantics..."
|
|
rc=0
|
|
sh "$ROOT/scripts/verify-permission-patterns.sh" || rc=$?
|
|
case "$rc" in
|
|
0) ;;
|
|
1) echo "ERROR: ENGINE DRIFT — opencode permission matching does NOT match documented semantics; installed definitions cannot be trusted. Aborting." >&2; exit 1 ;;
|
|
2) echo "ERROR: NO RUNTIME — no opencode binary found (set OPENCODE_BIN to override); install completed but CANNOT be verified." >&2 ;;
|
|
*) echo "ERROR: verifier exited unexpectedly (rc=$rc). Aborting." >&2; exit "$rc" ;;
|
|
esac
|
|
|
|
echo "==> Installed $copied/$EXPECTED_COUNT agents."
|
|
|
|
if (( backed_up > 0 )); then
|
|
echo "==> Previous versions of $backed_up file(s) saved under: $BACKUP_DIR"
|
|
fi
|
|
|
|
# --------------------------------------------------------------------- #
|
|
# Runtime tree install (D8)
|
|
# --------------------------------------------------------------------- #
|
|
echo "==> Installing runtime tree into $RUNTIME_ROOT"
|
|
|
|
mkdir -p "$RUNTIME_ROOT"
|
|
|
|
# Managed-subtree backup + retention: back up replaced runtime files before
|
|
# overwriting, under $RUNTIME_ROOT/.backup/<stamp>/, with KEEP_BACKUPS=5.
|
|
RT_BACKUP_DIR="$RUNTIME_ROOT/.backup/$STAMP"
|
|
managed_backed_up=0
|
|
backup_runtime_file() { # <relpath> — back up existing managed target before overwrite
|
|
local rel="$1"
|
|
if [ -e "$RUNTIME_ROOT/$rel" ]; then
|
|
mkdir -p "$RT_BACKUP_DIR/$(dirname "$rel")"
|
|
cp -p "$RUNTIME_ROOT/$rel" "$RT_BACKUP_DIR/$rel"
|
|
managed_backed_up=$((managed_backed_up + 1))
|
|
fi
|
|
}
|
|
|
|
# Stage-managed atomic install: write to .staging-<pid> then move.
|
|
install_managed_file() { # <src> <relpath>
|
|
local src="$1" rel="$2"
|
|
backup_runtime_file "$rel"
|
|
mkdir -p "$RUNTIME_ROOT/$(dirname "$rel")"
|
|
local stage="$RUNTIME_ROOT/.staging-$$-$(basename "$rel")"
|
|
cp -p "$src" "$stage"
|
|
mv -f "$stage" "$RUNTIME_ROOT/$rel"
|
|
}
|
|
|
|
# bin/ — memory-lifecycle.sh, repo-bootstrap.sh, verify-permission-patterns.sh, test suites
|
|
BIN_SRC_DIR="$ROOT/scripts"
|
|
mkdir -p "$RUNTIME_ROOT/bin"
|
|
for s in memory-lifecycle.sh repo-bootstrap.sh verify-permission-patterns.sh \
|
|
test-install.sh test-runtime.sh test-path-resolution.sh test-memory-isolation.sh \
|
|
test-agent-architecture.sh test-memory-system.sh test-repo-bootstrap.sh test-integration.sh; do
|
|
if [ -f "$BIN_SRC_DIR/$s" ]; then
|
|
install_managed_file "$BIN_SRC_DIR/$s" "bin/$s"
|
|
chmod 755 "$RUNTIME_ROOT/bin/$s"
|
|
echo " runtime: bin/$s"
|
|
fi
|
|
done
|
|
|
|
# skills/ — 12 skill dirs + SKILLS.md (managed, read-only)
|
|
if [ -d "$ROOT/skills" ]; then
|
|
mkdir -p "$RUNTIME_ROOT/skills"
|
|
# SKILLS.md
|
|
if [ -f "$ROOT/skills/SKILLS.md" ]; then
|
|
install_managed_file "$ROOT/skills/SKILLS.md" "skills/SKILLS.md"
|
|
fi
|
|
# each skill dir's SKILL.md
|
|
for d in "$ROOT"/skills/*/; do
|
|
[ -d "$d" ] || continue
|
|
name="$(basename "$d")"
|
|
if [ -f "$d/SKILL.md" ]; then
|
|
install_managed_file "$d/SKILL.md" "skills/$name/SKILL.md"
|
|
echo " runtime: skills/$name/SKILL.md"
|
|
fi
|
|
done
|
|
fi
|
|
|
|
# improvements/ — README only if absent; pending/applied/rejected never overwritten
|
|
mkdir -p "$RUNTIME_ROOT/improvements/pending" \
|
|
"$RUNTIME_ROOT/improvements/applied" \
|
|
"$RUNTIME_ROOT/improvements/rejected"
|
|
if [ -f "$ROOT/improvements/README.md" ] && [ ! -f "$RUNTIME_ROOT/improvements/README.md" ]; then
|
|
cp -p "$ROOT/improvements/README.md" "$RUNTIME_ROOT/improvements/README.md"
|
|
echo " runtime: improvements/README.md (first install)"
|
|
fi
|
|
|
|
# Runtime backup retention (same KEEP_BACKUPS=5 policy).
|
|
if (( managed_backed_up > 0 )); then
|
|
shopt -s nullglob
|
|
rt_backup_dirs=( "$RUNTIME_ROOT"/.backup/[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]_[0-9][0-9][0-9][0-9][0-9][0-9]_* )
|
|
shopt -u nullglob
|
|
rt_excess=$(( ${#rt_backup_dirs[@]} - KEEP_BACKUPS ))
|
|
if (( rt_excess > 0 )); then
|
|
for prune_dir in "${rt_backup_dirs[@]:0:rt_excess}"; do
|
|
[ -d "$prune_dir" ] || continue
|
|
rm -rf "$prune_dir"
|
|
echo " pruned old runtime backup: .backup/$(basename "$prune_dir")"
|
|
done
|
|
fi
|
|
fi
|
|
|
|
# --------------------------------------------------------------------- #
|
|
# install-manifest.json (version, date, installed files + sha256)
|
|
# --------------------------------------------------------------------- #
|
|
echo "==> Writing install-manifest.json"
|
|
VERSION="1.0.0"
|
|
{
|
|
echo "{"
|
|
echo " \"version\": \"$VERSION\","
|
|
echo " \"date\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\","
|
|
echo " \"agents_target\": \"$TARGET\","
|
|
echo " \"installed\": ["
|
|
first=1
|
|
for src_path in "${AGENT_FILES[@]}"; do
|
|
name="$(basename "$src_path")"
|
|
chk="$(sha256sum "$TARGET/$name" 2>/dev/null | awk '{print $1}' || echo 'unknown')"
|
|
[ "$first" = "0" ] && echo ","
|
|
printf ' { "file": "agents/%s", "target": "%s", "sha256": "%s" }' "$name" "$name" "$chk"
|
|
first=0
|
|
done
|
|
# runtime managed files
|
|
for s in memory-lifecycle.sh repo-bootstrap.sh verify-permission-patterns.sh; do
|
|
rp="bin/$s"
|
|
if [ -f "$RUNTIME_ROOT/$rp" ]; then
|
|
chk="$(sha256sum "$RUNTIME_ROOT/$rp" 2>/dev/null | awk '{print $1}' || echo 'unknown')"
|
|
echo ","
|
|
printf ' { "file": "runtime/%s", "target": "%s", "sha256": "%s" }' "$rp" "$rp" "$chk"
|
|
fi
|
|
done
|
|
echo ""
|
|
echo " ]"
|
|
echo "}"
|
|
} > "$MANIFEST"
|
|
|
|
# --------------------------------------------------------------------- #
|
|
# Idempotent shell-rc export of OPENCODE_DEV_AGENT_TEAM
|
|
# --------------------------------------------------------------------- #
|
|
RC_FILE=""
|
|
[ -f "$HOME/.bashrc" ] && RC_FILE="$HOME/.bashrc"
|
|
[ -z "$RC_FILE" ] && [ -f "$HOME/.profile" ] && RC_FILE="$HOME/.profile"
|
|
if [ -n "$RC_FILE" ]; then
|
|
if grep -q 'OPENCODE_DEV_AGENT_TEAM' "$RC_FILE"; then
|
|
echo "==> OPENCODE_DEV_AGENT_TEAM already exported in $RC_FILE (no change)."
|
|
else
|
|
printf '\n# dev_agent_team runtime root (added by install.sh)\nexport OPENCODE_DEV_AGENT_TEAM="${OPENCODE_DEV_AGENT_TEAM:-%s}"\n' "$RUNTIME_ROOT" >> "$RC_FILE"
|
|
echo "==> Added OPENCODE_DEV_AGENT_TEAM export to $RC_FILE"
|
|
fi
|
|
else
|
|
# No rc file: export via ~/.profile (create if missing) so GUI launches can
|
|
# still pick the var up from a login shell. Guarded/idempotent.
|
|
if [ ! -f "$HOME/.profile" ]; then
|
|
printf '# dev_agent_team runtime root (added by install.sh)\nexport OPENCODE_DEV_AGENT_TEAM="${OPENCODE_DEV_AGENT_TEAM:-%s}"\n' "$RUNTIME_ROOT" > "$HOME/.profile"
|
|
echo "==> Created $HOME/.profile with OPENCODE_DEV_AGENT_TEAM export"
|
|
else
|
|
echo "==> No shell rc (.bashrc/.profile) found to export OPENCODE_DEV_AGENT_TEAM; export it manually if needed."
|
|
fi
|
|
fi
|
|
|
|
# --------------------------------------------------------------------- #
|
|
# Install-time self-verification (D8 §5)
|
|
# --------------------------------------------------------------------- #
|
|
echo "==> Install-time verification"
|
|
# Permission gate: re-run verify-permission against transferred bin.
|
|
VP_RT="$RUNTIME_ROOT/bin/verify-permission-patterns.sh"
|
|
if [ -f "$VP_RT" ]; then
|
|
vprc=0
|
|
bash "$VP_RT" || vprc=$?
|
|
case "$vprc" in
|
|
0|2) echo " verify-permission-patterns: ok (rc=$vprc)" ;;
|
|
*) echo " WARNING: verify-permission-patterns rc=$vprc" >&2 ;;
|
|
esac
|
|
fi
|
|
|
|
# Presence checks on the runtime tree.
|
|
presence_ok=1
|
|
[ -x "$RUNTIME_ROOT/bin/memory-lifecycle.sh" ] || { echo " MISSING bin/memory-lifecycle.sh" >&2; presence_ok=0; }
|
|
[ -x "$RUNTIME_ROOT/bin/repo-bootstrap.sh" ] || { echo " MISSING bin/repo-bootstrap.sh" >&2; presence_ok=0; }
|
|
[ -d "$RUNTIME_ROOT/skills" ] || { echo " MISSING skills/" >&2; presence_ok=0; }
|
|
[ "$(find "$RUNTIME_ROOT/skills" -mindepth 1 -maxdepth 1 -type d 2>/dev/null | wc -l)" = "12" ] || { echo " skills/ has != 12 dirs" >&2; presence_ok=0; }
|
|
if [ "$presence_ok" = "1" ]; then
|
|
echo " runtime presence + permission checks: PASS"
|
|
else
|
|
echo " runtime presence + permission checks: some checks FAILED (see above)" >&2
|
|
fi
|
|
|
|
# Optional: run the new runtime test suites in a throwaway temp HOME via
|
|
# --self-test, so install self-proves before the user deletes the repo.
|
|
if [ "$SELF_TEST" = "1" ]; then
|
|
SELF_T="$(mktemp -d)"
|
|
SELF_TMP_HOME="$SELF_T/home"
|
|
SELF_SRC="$SELF_T/source"
|
|
mkdir -p "$SELF_TMP_HOME" "$SELF_SRC"
|
|
cp -r "$ROOT/agents" "$SELF_SRC/agents"
|
|
mkdir -p "$SELF_SRC/scripts" "$SELF_SRC/skills" "$SELF_SRC/improvements"
|
|
cp -p "$ROOT"/scripts/*.sh "$SELF_SRC/scripts/" 2>/dev/null || true
|
|
cp -r "$ROOT/skills/." "$SELF_SRC/skills/" 2>/dev/null || true
|
|
cp -r "$ROOT/improvements/." "$SELF_SRC/improvements/" 2>/dev/null || true
|
|
SELF_RT="$SELF_T/runtime"
|
|
SELF_AG="$SELF_T/agents"
|
|
echo " Running self-test (temp HOME install) ..."
|
|
env -i HOME="$SELF_TMP_HOME" \
|
|
OPENCODE_AGENTS_DIR="$SELF_AG" \
|
|
OPENCODE_DEV_AGENT_TEAM="$SELF_RT" \
|
|
TEAM_ROOT="$SELF_SRC" \
|
|
bash "$RUNTIME_ROOT/bin/test-install.sh" >"$SELF_T/install.log" 2>&1 && self_install=0 || self_install=$?
|
|
env -i HOME="$SELF_TMP_HOME" \
|
|
OPENCODE_AGENTS_DIR="$SELF_AG" \
|
|
OPENCODE_DEV_AGENT_TEAM="$SELF_RT" \
|
|
TEAM_ROOT="$SELF_SRC" \
|
|
bash "$RUNTIME_ROOT/bin/test-memory-isolation.sh" >"$SELF_T/mem.log" 2>&1 && self_mem=0 || self_mem=$?
|
|
if [ "$self_install" = "0" ] && [ "$self_mem" = "0" ]; then
|
|
echo " self-test: PASS (test-install + test-memory-isolation sub-suites green)"
|
|
else
|
|
echo " self-test: FAILED (install rc=$self_install, memory rc=$self_mem) — see $SELF_T logs" >&2
|
|
fi
|
|
rm -rf "$SELF_T"
|
|
fi
|
|
|
|
# --------------------------------------------------------------------- #
|
|
# Summary
|
|
# --------------------------------------------------------------------- #
|
|
echo "==> Installed runtime tree to $RUNTIME_ROOT"
|
|
echo "==> Upgrade: re-run ./scripts/install.sh from a newer source"
|
|
echo "==> Uninstall: ./scripts/install.sh --uninstall"
|
|
echo "==> The source checkout may now be deleted; agents + runtime remain installed."
|
|
|
|
if command -v opencode >/dev/null 2>&1; then
|
|
echo "==> Reminder: restart opencode, then run: opencode agent list"
|
|
else
|
|
echo "==> Note: 'opencode' not found on PATH; skipped restart/verify reminder."
|
|
fi
|
|
|
|
echo "==> Done."
|