Files
echo-v.05/echo-memory.plugin.src/skills/echo-memory/scripts/migrate.sh
T
Jason Stedwell 2fc3a0a80b ver-0.7
2026-06-19 21:12:14 -05:00

98 lines
3.9 KiB
Bash
Executable File

#!/usr/bin/env bash
# migrate.sh — bring an existing ECHO vault up to the plugin's current schema.
#
# Reads the marker's schema_version and applies each intervening migration in order.
# Migrations are idempotent and additive; every destructive step (DELETE) is gated
# behind --apply AND prints what it will do first. Default mode is a DRY-RUN plan.
#
# Usage:
# migrate.sh # print the migration plan (no changes)
# migrate.sh --apply # perform the migration (moves/deletes included)
#
# Env: ECHO_BASE, ECHO_KEY (via echo.sh).
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ECHO="$SCRIPT_DIR/echo.sh"
CURRENT_SCHEMA=2
APPLY=0
[ "${1:-}" = "--apply" ] && APPLY=1
[ -x "$ECHO" ] || chmod +x "$ECHO" 2>/dev/null || true
say() { echo "migrate: $*"; }
do_or_show() { # do_or_show "<human description>" cmd args...
local desc="$1"; shift
if [ "$APPLY" = "1" ]; then say "APPLY $desc"; "$@"; else say "PLAN $desc"; fi
}
# ---- Read current schema -----------------------------------------------------
if ! marker="$("$ECHO" get _agent/echo-vault.md 2>/dev/null)"; then
say "marker missing — vault not bootstrapped. Run bootstrap.sh, not migrate.sh."
exit 3
fi
FROM="$(printf '%s' "$marker" | sed -n 's/^schema_version:[[:space:]]*//p' | head -1)"
FROM="${FROM:-0}"
say "vault schema_version=$FROM, plugin schema=$CURRENT_SCHEMA $([ "$APPLY" = 1 ] && echo '(APPLY)' || echo '(dry-run)')"
if [ "$FROM" -ge "$CURRENT_SCHEMA" ] 2>/dev/null; then
say "up to date — nothing to do."
exit 0
fi
ls_files() { "$ECHO" ls "$1" 2>/dev/null | python3 -c 'import sys,json;print("\n".join(json.load(sys.stdin).get("files",[])))' 2>/dev/null || true; }
move() { # move SRC DST preserving content (PUT dst <- get src, then delete src)
local src="$1" dst="$2"
"$ECHO" get "$src" 2>/dev/null | "$ECHO" put "$dst" - >/dev/null
"$ECHO" delete "$src" >/dev/null
}
# ---- 0 -> 1 : control docs moved into the plugin -----------------------------
mig_0_1() {
say "[0->1] retire in-vault control docs (CLAUDE/BOOTSTRAP/STRUCTURE/index.md)"
for f in CLAUDE.md BOOTSTRAP.md STRUCTURE.md index.md; do
if ECHO_VERIFY=0 "$ECHO" get "$f" >/dev/null 2>&1; then
do_or_show "delete vault/$f (back it up outside the vault first)" "$ECHO" delete "$f"
fi
done
say "[0->1] reminder: scrub dangling [[CLAUDE]]/[[BOOTSTRAP]]/[[STRUCTURE]]/[[index]] links from ## Related sections (manual/agent step)."
}
# ---- 1 -> 2 : reviews/ folded into journal/ + _agent/health/ -----------------
mig_1_2() {
say "[1->2] fold reviews/ into journal/ and _agent/health/"
for f in $(ls_files reviews/weekly); do
[ "${f%.md}" != "$f" ] || continue
dst="journal/weekly/$(printf '%s' "$f" | sed 's/-review\.md$/.md/')"
do_or_show "move reviews/weekly/$f -> $dst" move "reviews/weekly/$f" "$dst"
done
for f in $(ls_files reviews/monthly); do
[ "${f%.md}" != "$f" ] || continue
case "$f" in
*vault-health.md) dst="_agent/health/$f" ;;
*) dst="journal/monthly/$f" ;;
esac
do_or_show "move reviews/monthly/$f -> $dst" move "reviews/monthly/$f" "$dst"
done
for period in quarterly annual; do
for f in $(ls_files "reviews/$period"); do
[ "${f%.md}" != "$f" ] || continue
do_or_show "move reviews/$period/$f -> journal/$period/$f" move "reviews/$period/$f" "journal/$period/$f"
done
done
say "[1->2] reminder: update inbound [[reviews/...]] wikilinks in ## Related sections (manual/agent step)."
}
[ "$FROM" -lt 1 ] && mig_0_1
[ "$FROM" -lt 2 ] && mig_1_2
# ---- Stamp the marker --------------------------------------------------------
do_or_show "set _agent/echo-vault.md schema_version -> $CURRENT_SCHEMA" \
"$ECHO" fm _agent/echo-vault.md schema_version "$CURRENT_SCHEMA"
if [ "$APPLY" = "1" ]; then
say "migration complete -> schema $CURRENT_SCHEMA. Run vault-lint.sh to confirm invariants."
else
say "dry-run only. Re-run with --apply to perform the moves/deletes above."
fi