fix(install): backup existing agents dirs before clobber

Previously cp -f clobbered user-edited _blocks/, _templates/, _assembler/,
hooks/, and skills/ silently — violating the README's "idempotent" claim
on re-run. Now each of those targets is snapshotted to a timestamped
<target>.bak-$(date +%s)/ sibling before overwrite, but only when the
target actually contains regular files (freshly-mkdir'd scaffolds are
skipped). _manifests/ unchanged — already uses per-file skip-if-exists.
Kit source ($KIT_DIR) is never backed up.
This commit is contained in:
Parfii-bot 2026-04-21 02:41:26 +08:00
parent 0b901cf2f9
commit b1a77f393a

View file

@ -14,6 +14,23 @@ say() { printf '\033[1;36m[install]\033[0m %s\n' "$*"; }
warn() { printf '\033[1;33m[warn]\033[0m %s\n' "$*"; }
err() { printf '\033[1;31m[error]\033[0m %s\n' "$*" >&2; }
# Backup a populated target directory to a timestamped sibling before clobber.
# No-op if target is absent or contains no regular files (recursively). This
# means freshly-mkdir'd scaffolds are NOT backed up — only real user content.
# Only called on $AGENTS_DIR/_blocks, _templates, _assembler, $HOOKS_DIR,
# $SKILLS_DIR — never on $KIT_DIR source.
backup_dir() {
local target="$1"
[ -d "$target" ] || return 0
# No regular files anywhere under target → nothing worth preserving
if [ -z "$(find "$target" -type f -print -quit 2>/dev/null)" ]; then
return 0
fi
local backup="${target}.bak-$(date +%s)"
cp -a "$target" "$backup"
say "backed up existing $target to $backup"
}
# --- prerequisites ----------------------------------------------------------
say "checking prerequisites"
if ! command -v cargo >/dev/null 2>&1; then
@ -44,6 +61,7 @@ mkdir -p \
# --- copy blocks (overwrite ours; blocks are SSoT from kit) ----------------
say "copying shared blocks -> $AGENTS_DIR/_blocks/"
backup_dir "$AGENTS_DIR/_blocks"
cp -f "$KIT_DIR/_blocks/"*.md "$AGENTS_DIR/_blocks/"
# --- copy generic manifests, DO NOT overwrite user's existing manifests -----
@ -63,11 +81,13 @@ say " copied $copied, skipped $skipped (already present)"
# --- copy template ---------------------------------------------------------
if compgen -G "$KIT_DIR/_templates/*.template" >/dev/null; then
say "copying specialist template"
backup_dir "$AGENTS_DIR/_templates"
cp -f "$KIT_DIR/_templates/"*.template "$AGENTS_DIR/_templates/"
fi
# --- copy assembler source (always refresh) --------------------------------
say "copying assembler source"
backup_dir "$AGENTS_DIR/_assembler"
cp -f "$KIT_DIR/_assembler/Cargo.toml" "$AGENTS_DIR/_assembler/"
cp -f "$KIT_DIR/_assembler/src/"*.rs "$AGENTS_DIR/_assembler/src/"
if [[ -f "$KIT_DIR/_assembler/.gitignore" ]]; then
@ -76,6 +96,7 @@ fi
# --- copy hooks (refresh; hooks are logic, not config) ---------------------
say "copying hooks -> $HOOKS_DIR/"
backup_dir "$HOOKS_DIR"
for h in assemble-agents.sh assemble-validate.sh no-hand-edit-agents.sh; do
cp -f "$KIT_DIR/hooks/$h" "$HOOKS_DIR/$h"
chmod +x "$HOOKS_DIR/$h"
@ -84,6 +105,7 @@ done
# --- copy skills -----------------------------------------------------------
if [[ -d "$KIT_DIR/skills" ]]; then
say "copying skills"
backup_dir "$SKILLS_DIR"
for skill_dir in "$KIT_DIR/skills/"*/; do
[ -d "$skill_dir" ] || continue
skill_name="$(basename "$skill_dir")"