diff --git a/packages/mosaic/framework/tools/_scripts/mosaic-sync-skills b/packages/mosaic/framework/tools/_scripts/mosaic-sync-skills index f044988..be343e2 100755 --- a/packages/mosaic/framework/tools/_scripts/mosaic-sync-skills +++ b/packages/mosaic/framework/tools/_scripts/mosaic-sync-skills @@ -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 — 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"