fix: auto-migrate customized skills to skills-local/ on sync (#359)
Some checks failed
ci/woodpecker/push/publish Pipeline failed
ci/woodpecker/push/ci Pipeline failed

This commit was merged in pull request #359.
This commit is contained in:
2026-04-03 02:11:03 +00:00
parent 0cf80dab8c
commit a23c117ea4

View File

@@ -56,38 +56,66 @@ if [[ $fetch -eq 1 ]]; then
if [[ -d "$SKILLS_REPO_DIR/.git" ]]; then
echo "[mosaic-skills] Updating skills source: $SKILLS_REPO_DIR"
# Detect ANY dirty state: modified, staged, or untracked files.
# Use git status --porcelain which catches everything pull --rebase cares about.
# ── Detect dirty state ──────────────────────────────────────────────
dirty=""
dirty="$(git -C "$SKILLS_REPO_DIR" status --porcelain 2>/dev/null || true)"
if [[ -n "$dirty" ]]; then
echo "[mosaic-skills] Stashing local changes (including untracked)..."
git -C "$SKILLS_REPO_DIR" stash push -q --include-untracked -m "mosaic-sync-skills auto-stash" 2>/dev/null || {
echo "[mosaic-skills] WARN: stash failed — skipping pull, using existing checkout" >&2
dirty="skip-pull"
}
fi
# ── Auto-migrate customized skills to skills-local/ ─────────────
# Instead of stash/pop (fragile, merge conflicts), we:
# 1. Identify which skill dirs contain user edits
# 2. Copy those full skill dirs into skills-local/ (preserving edits)
# 3. Reset the repo clean so pull always succeeds
# 4. skills-local/ takes precedence during linking, so edits win
if [[ "$dirty" != "skip-pull" ]]; then
if ! git -C "$SKILLS_REPO_DIR" pull --rebase 2>/dev/null; then
echo "[mosaic-skills] WARN: pull failed — continuing with existing checkout" >&2
# Abort any in-progress rebase so the repo isn't left in a broken state
git -C "$SKILLS_REPO_DIR" rebase --abort 2>/dev/null || true
SOURCE_SKILLS_SUBDIR="$SKILLS_REPO_DIR/skills"
migrated=()
while IFS= read -r line; do
# porcelain format: XY <path> — extract the file path
file="${line:3}"
# Only migrate files under skills/ subdir in the repo
if [[ "$file" == skills/* ]]; then
# Extract the skill directory name (first path component after skills/)
skill_name="${file#skills/}"
skill_name="${skill_name%%/*}"
# Skip if already migrated this skill in this run
local_skill_dir="$MOSAIC_LOCAL_SKILLS_DIR/$skill_name"
if [[ -d "$local_skill_dir" ]]; then
continue
fi
# Skip if skill_name is empty or hidden
if [[ -z "$skill_name" || "$skill_name" == .* ]]; then
continue
fi
# Copy the skill (with user's edits) from repo working tree to skills-local/
if [[ -d "$SOURCE_SKILLS_SUBDIR/$skill_name" ]]; then
cp -R "$SOURCE_SKILLS_SUBDIR/$skill_name" "$local_skill_dir"
migrated+=("$skill_name")
fi
fi
done <<< "$dirty"
if [[ ${#migrated[@]} -gt 0 ]]; then
echo "[mosaic-skills] Migrated ${#migrated[@]} customized skill(s) to skills-local/:"
for s in "${migrated[@]}"; do
echo " → $MOSAIC_LOCAL_SKILLS_DIR/$s"
done
echo "[mosaic-skills] Your edits are preserved there and take precedence over canonical."
fi
# Reset repo to clean state so pull always works
echo "[mosaic-skills] Resetting source repo to clean state..."
git -C "$SKILLS_REPO_DIR" checkout . 2>/dev/null || true
git -C "$SKILLS_REPO_DIR" clean -fd 2>/dev/null || true
fi
# Restore stashed changes if we stashed anything
if [[ -n "$dirty" && "$dirty" != "skip-pull" ]]; then
echo "[mosaic-skills] Restoring local changes..."
git -C "$SKILLS_REPO_DIR" stash pop -q 2>/dev/null || \
echo "[mosaic-skills] WARN: stash pop had conflicts — check $SKILLS_REPO_DIR" >&2
fi
# Hint: customized skills belong in skills-local/, not in the canonical repo
if [[ -n "$dirty" ]]; then
echo "[mosaic-skills] TIP: Put customized skills in $MOSAIC_LOCAL_SKILLS_DIR/ instead"
echo "[mosaic-skills] Files there are never overwritten and take precedence."
if ! git -C "$SKILLS_REPO_DIR" pull --rebase 2>/dev/null; then
echo "[mosaic-skills] WARN: pull failed — continuing with existing checkout" >&2
git -C "$SKILLS_REPO_DIR" rebase --abort 2>/dev/null || true
fi
else
echo "[mosaic-skills] Cloning skills source to: $SKILLS_REPO_DIR"