#!/usr/bin/env bash set -euo pipefail log_info(){ echo "[INFO] $(date -Iseconds) $*"; } log_warn(){ echo "[WARN] $(date -Iseconds) $*" >&2; } log_error(){ echo "[ERROR] $(date -Iseconds) $*" >&2; } die(){ log_error "$*"; exit 1; } need(){ command -v "$1" >/dev/null 2>&1 || die "Missing required tool: $1"; } json_escape() { local s="$1" s="${s//\\/\\\\}" s="${s//\"/\\\"}" s="${s//$'\n'/\\n}" s="${s//$'\r'/\\r}" s="${s//$'\t'/\\t}" printf "%s" "$s" } b3_file() { local f="$1" if command -v b3sum >/dev/null 2>&1; then b3sum "$f" | awk '{print $1}' elif command -v blake3 >/dev/null 2>&1; then blake3 "$f" else die "Need BLAKE3 tool: b3sum (preferred) or blake3." fi } confirm_gate() { : "${DRY_RUN:=1}" : "${REQUIRE_CONFIRM:=1}" : "${CONFIRM_PHRASE:=I UNDERSTAND THIS CAN OVERWRITE RECOVERY TARGETS}" [[ "$DRY_RUN" == "0" ]] || die "DRY_RUN=$DRY_RUN (set DRY_RUN=0 to apply)." if [[ "$REQUIRE_CONFIRM" == "1" ]]; then echo "Type to confirm:" echo " $CONFIRM_PHRASE" read -r input [[ "$input" == "$CONFIRM_PHRASE" ]] || die "Confirmation phrase mismatch." fi } resolve_run_dir() { local backup_skill_dir="$1" local run_dir="${2:-}" if [[ -n "$run_dir" ]]; then echo "$run_dir" return 0 fi local ptr="$backup_skill_dir/outputs/last_run_dir.txt" [[ -f "$ptr" ]] || die "RUN_DIR not set and missing pointer: $ptr" cat "$ptr" }