0198ab6881
Attach/onboard sandbox from agents/hermes, additive OpenShell policy overlays, and nemohermes skill install for scaffold skills. No doctor/connect and no push.
198 lines
6.6 KiB
Bash
Executable File
198 lines
6.6 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# scripts/install/s5-policy-skills.sh — S5: policy apply + skills sync
|
|
#
|
|
# Applies Lumina policy overlays and syncs skills into the sandbox.
|
|
# Additive only: never removes existing presets.
|
|
#
|
|
# Platform-first: all mutations via nemohermes.
|
|
#
|
|
# Usage:
|
|
# ./scripts/install/s5-policy-skills.sh
|
|
# ./scripts/install/s5-policy-skills.sh --policy-only
|
|
# ./scripts/install/s5-policy-skills.sh --skills-only
|
|
# ./scripts/install/s5-policy-skills.sh --help
|
|
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
|
|
# Source shared helpers
|
|
# shellcheck source=../lib/common.sh
|
|
source "$SCRIPT_DIR/../lib/common.sh"
|
|
# shellcheck source=../lib/env.sh
|
|
source "$SCRIPT_DIR/../lib/env.sh"
|
|
|
|
# ── Defaults ───────────────────────────────────────────────────────────────
|
|
DO_POLICY=1
|
|
DO_SKILLS=1
|
|
POLICY_ONLY_SET=0
|
|
SKILLS_ONLY_SET=0
|
|
|
|
# ── Parse args ─────────────────────────────────────────────────────────────
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--help|-h)
|
|
cat <<EOF
|
|
Usage: $(basename "$0") [OPTIONS]
|
|
|
|
S5: Apply policy overlays and sync skills into the sandbox.
|
|
|
|
Options:
|
|
--policy-only Apply policy overlays only (skip skills sync)
|
|
--skills-only Sync skills only (skip policy apply)
|
|
--help Show this help
|
|
|
|
Examples:
|
|
$(basename "$0") # policy + skills
|
|
$(basename "$0") --policy-only # policy only
|
|
$(basename "$0") --skills-only # skills only
|
|
EOF
|
|
exit 0
|
|
;;
|
|
--policy-only)
|
|
DO_POLICY=1
|
|
DO_SKILLS=0
|
|
POLICY_ONLY_SET=1
|
|
shift
|
|
;;
|
|
--skills-only)
|
|
DO_POLICY=0
|
|
DO_SKILLS=1
|
|
SKILLS_ONLY_SET=1
|
|
shift
|
|
;;
|
|
*)
|
|
log_error "Unknown argument: $1"
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
# ── Validate mutually exclusive flags ──────────────────────────────────────
|
|
if [[ $POLICY_ONLY_SET -eq 1 && $SKILLS_ONLY_SET -eq 1 ]]; then
|
|
log_error "--policy-only and --skills-only are mutually exclusive"
|
|
exit 1
|
|
fi
|
|
if [[ $DO_POLICY -eq 0 && $DO_SKILLS -eq 0 ]]; then
|
|
log_error "Internal error: both DO_POLICY and DO_SKILLS are disabled"
|
|
exit 1
|
|
fi
|
|
|
|
log_section "S5: Policy + Skills sync"
|
|
|
|
# ── Load .env ──────────────────────────────────────────────────────────────
|
|
load_env
|
|
|
|
# ── Validate required keys ─────────────────────────────────────────────────
|
|
validate_env || exit 1
|
|
|
|
# ── Check CLI prerequisites ────────────────────────────────────────────────
|
|
require_cmd nemohermes "Install nemohermes CLI (part of NemoClaw platform)"
|
|
|
|
SANDBOX_NAME="$(get_sandbox_name)"
|
|
POLICY_DIR="$REPO_ROOT/policy/openshell/overlays"
|
|
SKILLS_DIR="$REPO_ROOT/skills"
|
|
|
|
# ── Verify sandbox exists ──────────────────────────────────────────────────
|
|
if ! nemohermes "$SANDBOX_NAME" status &>/dev/null 2>&1; then
|
|
log_error "Sandbox '$SANDBOX_NAME' not found. Run S4 first."
|
|
exit 1
|
|
fi
|
|
|
|
# ── Policy apply ───────────────────────────────────────────────────────────
|
|
apply_policy() {
|
|
log_section "S5a: Apply policy overlays"
|
|
|
|
if [[ ! -d "$POLICY_DIR" ]]; then
|
|
log_warn "Policy overlays directory not found: $POLICY_DIR"
|
|
return 0
|
|
fi
|
|
|
|
# Apply inference overlay (always apply at S5)
|
|
local inference_policy="$POLICY_DIR/inference.yaml"
|
|
if [[ -f "$inference_policy" ]]; then
|
|
log_info "Applying inference policy overlay…"
|
|
if nemohermes "$SANDBOX_NAME" policy-add --from-file "$inference_policy" --yes 2>&1; then
|
|
log_info "Inference policy overlay applied."
|
|
else
|
|
log_warn "Inference policy overlay may already be applied (idempotent)."
|
|
fi
|
|
else
|
|
log_warn "Inference policy overlay not found: $inference_policy"
|
|
fi
|
|
|
|
# List current policy for verification
|
|
log_info "Current policy presets:"
|
|
nemohermes "$SANDBOX_NAME" policy-list 2>&1 || log_warn "Could not list policy presets"
|
|
|
|
log_info "S5a policy apply complete."
|
|
}
|
|
|
|
# ── Skills sync ────────────────────────────────────────────────────────────
|
|
sync_skills() {
|
|
log_section "S5b: Sync skills"
|
|
|
|
if [[ ! -d "$SKILLS_DIR" ]]; then
|
|
log_warn "Skills directory not found: $SKILLS_DIR"
|
|
return 0
|
|
fi
|
|
|
|
local skill_count=0
|
|
local skill_ok=0
|
|
local skill_skip=0
|
|
local skill_fail=0
|
|
|
|
# Iterate skill directories (skip _lib and hidden dirs)
|
|
for skill_dir in "$SKILLS_DIR"/*/; do
|
|
# Skip if not a directory
|
|
[[ -d "$skill_dir" ]] || continue
|
|
|
|
local skill_name
|
|
skill_name="$(basename "$skill_dir")"
|
|
|
|
# Skip _lib (shared library, not a skill)
|
|
if [[ "$skill_name" == "_lib" ]]; then
|
|
log_info "Skipping _lib (shared library)"
|
|
continue
|
|
fi
|
|
|
|
skill_count=$((skill_count + 1))
|
|
|
|
# Check for SKILL.md (required by nemohermes skill install)
|
|
local skill_md="$skill_dir/SKILL.md"
|
|
if [[ ! -f "$skill_md" ]]; then
|
|
log_warn "Skipping '$skill_name': no SKILL.md found"
|
|
skill_skip=$((skill_skip + 1))
|
|
continue
|
|
fi
|
|
|
|
log_info "Installing skill: $skill_name"
|
|
if nemohermes "$SANDBOX_NAME" skill install "$skill_dir" 2>&1; then
|
|
log_info " ✓ $skill_name installed"
|
|
skill_ok=$((skill_ok + 1))
|
|
else
|
|
log_warn " ✗ $skill_name failed (may already be installed)"
|
|
skill_fail=$((skill_fail + 1))
|
|
fi
|
|
done
|
|
|
|
log_info "Skills sync summary: $skill_count found, $skill_ok installed, $skill_skip skipped, $skill_fail failed"
|
|
|
|
if [[ $skill_count -eq 0 ]]; then
|
|
log_warn "No skill directories found in $SKILLS_DIR"
|
|
fi
|
|
|
|
log_info "S5b skills sync complete."
|
|
}
|
|
|
|
# ── Execute ────────────────────────────────────────────────────────────────
|
|
if [[ $DO_POLICY -eq 1 ]]; then
|
|
apply_policy
|
|
fi
|
|
|
|
if [[ $DO_SKILLS -eq 1 ]]; then
|
|
sync_skills
|
|
fi
|
|
|
|
log_info "S5 complete."
|