From b1a77f393a56234c8143b58cdaeaea58dfa98685 Mon Sep 17 00:00:00 2001 From: Parfii-bot Date: Tue, 21 Apr 2026 02:41:26 +0800 Subject: [PATCH] fix(install): backup existing agents dirs before clobber MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 .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. --- install.sh | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/install.sh b/install.sh index c8f69fe..48a9c4b 100755 --- a/install.sh +++ b/install.sh @@ -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")"