#!/usr/bin/env bash # Deploy initial skills to app-instance containers without overwriting existing skill directories. # Usage: # ./scripts/deploy-initial-skills.sh # ./scripts/deploy-initial-skills.sh app-instance-terminaltest set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" SKILL_SOURCE="${SKILL_SOURCE:-${REPO_ROOT}/skills}" SKILL_EXCLUDE="${SKILL_EXCLUDE:-officebench-mcp}" if [[ ! -f "${SKILL_SOURCE}/_index/published.json" ]]; then printf '[deploy-initial-skills] missing published index: %s\n' "${SKILL_SOURCE}/_index/published.json" >&2 exit 1 fi if [[ $# -gt 0 ]]; then DOCKER_NAMES=("$@") else mapfile -t DOCKER_NAMES < <(docker ps --format '{{.Names}}' | grep '^app-instance-' | sort) fi if [[ "${#DOCKER_NAMES[@]}" -eq 0 ]]; then printf '[deploy-initial-skills] no app-instance containers found\n' >&2 exit 1 fi SKILLS_JSON="$(SKILL_SOURCE="$SKILL_SOURCE" SKILL_EXCLUDE="$SKILL_EXCLUDE" python3 - <<'PY' import json import os from pathlib import Path source = Path(os.environ["SKILL_SOURCE"]) excluded = {item.strip() for item in os.environ.get("SKILL_EXCLUDE", "").split(",") if item.strip()} items = json.loads((source / "_index" / "published.json").read_text(encoding="utf-8")).get("items", []) print(json.dumps([str(item).strip() for item in items if str(item).strip() and str(item).strip() not in excluded])) PY )" mapfile -t SKILLS < <(SKILLS_JSON="$SKILLS_JSON" python3 - <<'PY' import json import os for item in json.loads(os.environ["SKILLS_JSON"]): print(item) PY ) for container in "${DOCKER_NAMES[@]}"; do printf '==> Deploying initial skills to %s...\n' "$container" docker exec "$container" mkdir -p /root/.beaver/workspace/skills/_index for skill in "${SKILLS[@]}"; do if [[ ! -d "$SKILL_SOURCE/$skill" ]]; then printf ' ! missing source skill: %s\n' "$skill" continue fi if docker exec "$container" test -e "/root/.beaver/workspace/skills/$skill"; then printf ' = %s\n' "$skill" continue fi docker cp "$SKILL_SOURCE/$skill" "$container":/root/.beaver/workspace/skills/ printf ' + %s\n' "$skill" done docker exec -i -e SKILLS_JSON="$SKILLS_JSON" "$container" python3 - <<'PY' import json import os from pathlib import Path idx = Path("/root/.beaver/workspace/skills/_index/published.json") try: existing = json.loads(idx.read_text(encoding="utf-8")) if idx.exists() else {"items": []} except json.JSONDecodeError: existing = {"items": []} items = existing.get("items") if not isinstance(items, list): items = [] merged = [] for item in [*items, *json.loads(os.environ["SKILLS_JSON"])]: text = str(item).strip() if text and text not in merged: merged.append(text) idx.parent.mkdir(parents=True, exist_ok=True) idx.write_text(json.dumps({"items": merged}, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") print(f" index updated: {len(merged)} skills") PY if [[ -f "$SKILL_SOURCE/_index/disabled.json" ]]; then docker cp "$SKILL_SOURCE/_index/disabled.json" "$container":/root/.beaver/workspace/skills/_index/disabled.json fi printf ' [done]\n' done printf '\nDone. Containers: %s\n' "${DOCKER_NAMES[*]}"