diff --git a/install.sh b/install.sh index 5dc56d5..05f7145 100755 --- a/install.sh +++ b/install.sh @@ -222,3 +222,168 @@ scan_skills() { ok "${#SKILLS_LIST[@]} skill(s) trouvé(s)" } + +# ── État du menu ────────────────────────────────────────────────── +state_init() { + : > "$STATE_FILE" + for entry in "${SKILLS_LIST[@]}"; do + local key="${entry//|/_}" + echo "${key}=local" >> "$STATE_FILE" + done +} + +state_get() { + grep "^${1}=" "$STATE_FILE" 2>/dev/null | cut -d'=' -f2 +} + +state_cycle() { + local key="$1" etat="$2" + local current; current=$(state_get "$key") + local next + case "$current" in + local) next="global" ;; + global) next="skip" ;; + skip) [[ "$etat" == "upd" ]] && next="update" || next="local" ;; + update) next="local" ;; + *) next="local" ;; + esac + sed -i "s|^${key}=.*|${key}=${next}|" "$STATE_FILE" +} + +# ── Formatage ligne menu ────────────────────────────────────────── +format_skill_line() { + local entry="$1" + local cat skill agent etat repo_ver local_ver + IFS='|' read -r cat skill agent etat repo_ver local_ver <<< "$entry" + local key="${cat}_${skill}_${agent}" + local action; action=$(state_get "$key") + + local ico_etat color_etat + case "$etat" in + ok) ico_etat="$ICO_OK"; color_etat="$GRV_GREEN" ;; + upd) ico_etat="$ICO_UPD"; color_etat="$GRV_YELLOW" ;; + new) ico_etat="$ICO_NEW"; color_etat="$GRV_AQUA" ;; + *) ico_etat="$ICO_NA"; color_etat="$GRV_GRAY" ;; + esac + + local ico_action color_action + case "$action" in + local) ico_action="$ICO_LOCAL"; color_action="$GRV_GREEN" ;; + global) ico_action="$ICO_GLOBAL"; color_action="$GRV_BLUE" ;; + skip) ico_action="$ICO_SKIP"; color_action="$GRV_GRAY" ;; + update) ico_action="$ICO_UPD"; color_action="$GRV_YELLOW" ;; + *) ico_action="$ICO_SKIP"; color_action="$GRV_GRAY" ;; + esac + + local ver_info="" + [[ "$etat" == "upd" ]] && ver_info=" (${local_ver}→${repo_ver})" + [[ "$etat" == "new" ]] && ver_info=" (v${repo_ver})" + + printf "${color_etat}%s${RESET} %-35s ${GRV_GRAY}[%s]${RESET} ${color_action}%s${RESET}%s\n" \ + "$ico_etat" "${cat}/${skill}" "$agent" "$ico_action" "$ver_info" +} + +# ── Menu fzf principal ──────────────────────────────────────────── +run_menu() { + header "Sélection des skills" + state_init + + local cycle_script="/tmp/skills_cycle_$$.sh" + local list_script="/tmp/skills_list_$$.sh" + local fns_file="/tmp/skills_fns_$$.sh" + + # Script de cycle d'état (appelé par fzf via execute-silent) + cat > "$cycle_script" << 'CYCLE_EOF' +#!/usr/bin/env bash +STATE_FILE="$1" +KEY="$2" +ETAT="$3" +current=$(grep "^${KEY}=" "$STATE_FILE" 2>/dev/null | cut -d'=' -f2) +case "$current" in + local) next="global" ;; + global) next="skip" ;; + skip) [[ "$ETAT" == "upd" ]] && next="update" || next="local" ;; + update) next="local" ;; + *) next="local" ;; +esac +sed -i "s|^${KEY}=.*|${KEY}=${next}|" "$STATE_FILE" +CYCLE_EOF + chmod +x "$cycle_script" + + # Exporter fonctions et variables dans un fichier source + { + declare -f format_skill_line state_get + declare -p GRV_GREEN GRV_YELLOW GRV_AQUA GRV_GRAY GRV_BLUE RESET \ + ICO_OK ICO_UPD ICO_NEW ICO_NA ICO_LOCAL ICO_GLOBAL ICO_SKIP + echo "STATE_FILE='$STATE_FILE'" + echo "SKILLS_LIST=($(printf '"%s" ' "${SKILLS_LIST[@]}"))" + } > "$fns_file" + + # Script générateur de liste pour fzf --reload + cat > "$list_script" << LIST_EOF +#!/usr/bin/env bash +source "$fns_file" +for entry in "\${SKILLS_LIST[@]}"; do + format_skill_line "\$entry" +done +LIST_EOF + chmod +x "$list_script" + + local legend + legend=$(echo -e "${GRV_GRAY}État: ${GRV_GREEN}✓ installé ${GRV_YELLOW}↑ MAJ ${GRV_AQUA}+ nouveau Action: ${GRV_GREEN}●L local ${GRV_BLUE}●G global ${GRV_GRAY}○ ignorer TAB=changer ENTER=confirmer ESC=quitter${RESET}") + + fzf \ + --ansi \ + --prompt="Skills > " \ + --header="$legend" \ + --bind="tab:execute-silent($cycle_script '$STATE_FILE' \$(echo {} | awk '{print \$2}' | tr '/' '_' | sed 's/-/_/g')_\$(echo {} | awk '{print \$3}' | tr -d '[]') \$(echo {} | awk '{print \$4}'))+reload($list_script)" \ + < <(bash "$list_script") > /dev/null || true + + rm -f "$cycle_script" "$list_script" "$fns_file" +} + +# ── Installation ────────────────────────────────────────────────── +install_selected() { + header "Installation" + local count_install=0 count_update=0 count_skip=0 + + for entry in "${SKILLS_LIST[@]}"; do + local cat skill agent etat repo_ver local_ver + IFS='|' read -r cat skill agent etat repo_ver local_ver <<< "$entry" + local key="${cat}_${skill}_${agent}" + local action; action=$(state_get "$key") + + if [[ "$action" == "skip" ]]; then + (( count_skip++ )) || true + continue + fi + + local scope="$action" + [[ "$action" == "update" ]] && scope="local" + + local src="${REPO_DIR}/skills/${cat}/${skill}/${agent}.md" + local dest; dest=$(get_dest_path "$cat" "$skill" "$agent" "$scope") + + debug "Copie $src → $dest" + + if [[ "$SKILLS_DRY_RUN" == "1" ]]; then + info "[DRY-RUN] cp $src → $dest" + else + mkdir -p "$(dirname "$dest")" + cp "$src" "$dest" + fi + + if [[ "$action" == "update" ]]; then + ok "Mis à jour : ${cat}/${skill} [${agent}] ${local_ver}→${repo_ver}" + (( count_update++ )) || true + else + ok "Installé : ${cat}/${skill} [${agent}] → ${scope}" + (( count_install++ )) || true + fi + done + + echo -e "\n${GRV_PURPLE}╔══ Bilan ══╗${RESET}" + echo -e " ${GRV_GREEN}${ICO_OK} $count_install installé(s)${RESET}" + echo -e " ${GRV_YELLOW}${ICO_UPD} $count_update mis à jour${RESET}" + echo -e " ${GRV_GRAY}${ICO_SKIP} $count_skip ignoré(s)${RESET}" +}