diff --git a/install.sh b/install.sh index c2d16c0..046870a 100755 --- a/install.sh +++ b/install.sh @@ -189,7 +189,8 @@ select_agents() { --delimiter='\t' \ --with-nth=2.. \ --prompt="Agents > " \ - --header="$(echo -e "${GRV_GRAY}TAB=sélectionner/désélectionner ENTER=valider${RESET}")") || true + --bind="esc:abort" \ + --header="$(echo -e "${GRV_GRAY}TAB=sélectionner/désélectionner ENTER=valider ESC=quitter${RESET}")") || { echo; err "Annulé."; exit 0; } DETECTED_AGENTS=() while IFS=$'\t' read -r agent_name _; do @@ -416,11 +417,14 @@ run_menu() { done local space_script="/tmp/skills_space_$$.sh" + local fold_script="/tmp/skills_fold_$$.sh" local tab_script="/tmp/skills_tab_$$.sh" local list_script="/tmp/skills_list_$$.sh" local fns_file="/tmp/skills_fns_$$.sh" local preview_script="/tmp/skills_preview_$$.sh" local copy_script="/tmp/skills_copy_$$.sh" + local mode_file="/tmp/skills_mode_$$.txt" + echo "repo" > "$mode_file" # Exporter fonctions et données dans un fichier source partagé { @@ -430,6 +434,7 @@ run_menu() { echo "STATE_FILE='$STATE_FILE'" echo "REPO_DIR='$REPO_DIR'" echo "COLLAPSED_FILE='$COLLAPSED_FILE'" + echo "MODE_FILE='$mode_file'" echo "SKILLS_LIST=($(printf '"%s" ' "${SKILLS_LIST[@]}"))" echo "DETECTED_AGENTS=($(printf '"%s" ' "${DETECTED_AGENTS[@]}"))" } > "$fns_file" @@ -457,8 +462,8 @@ SPACE_EOF sed -i "s|FNSFILE|$fns_file|" "$space_script" chmod +x "$space_script" - # Script TAB : plier/déplier une catégorie - cat > "$tab_script" << 'TAB_EOF' + # Script X : plier/déplier une catégorie (anciennement TAB) + cat > "$fold_script" << 'FOLD_EOF' #!/usr/bin/env bash source "FNSFILE" td="$1"; type="${td%%:*}"; value="${td#*:}" @@ -476,56 +481,100 @@ if grep -qx "$cat_name" "$COLLAPSED_FILE" 2>/dev/null; then else echo "$cat_name" >> "$COLLAPSED_FILE" fi +FOLD_EOF + sed -i "s|FNSFILE|$fns_file|" "$fold_script" + chmod +x "$fold_script" + + # Script TAB : basculer entre section DÉPÔT et section GLOBAL + cat > "$tab_script" << 'TAB_EOF' +#!/usr/bin/env bash +source "FNSFILE" +mode=$(cat "$MODE_FILE" 2>/dev/null || echo "repo") +[[ "$mode" == "repo" ]] && echo "global" > "$MODE_FILE" || echo "repo" > "$MODE_FILE" TAB_EOF sed -i "s|FNSFILE|$fns_file|" "$tab_script" chmod +x "$tab_script" - # Script LIST : générateur d'arbre pour fzf --reload + # Script LIST : deux sections (DÉPÔT / GLOBAL) avec bascule via MODE_FILE cat > "$list_script" << 'LIST_EOF' #!/usr/bin/env bash source "FNSFILE" -declare -A cat_map -declare -a cat_order -for i in "${!SKILLS_LIST[@]}"; do - cat="${SKILLS_LIST[$i]%%|*}" - [[ -z "${cat_map[$cat]+x}" ]] && { cat_order+=("$cat"); cat_map[$cat]=""; } - cat_map[$cat]+=" $i" -done -for cat in "${cat_order[@]}"; do - indices=(${cat_map[$cat]}) - collapsed=0 - grep -qx "$cat" "$COLLAPSED_FILE" 2>/dev/null && collapsed=1 - format_category_header "$cat" "${#indices[@]}" "$collapsed" - [[ "$collapsed" == "1" ]] && continue - for idx in "${indices[@]}"; do - format_skill_line "${SKILLS_LIST[$idx]}" "$idx" - done -done -# Footer : skills déjà installés globalement +mode=$(cat "$MODE_FILE" 2>/dev/null || echo "repo") + declare -A agent_dir_map agent_dir_map[claude-code]="$HOME/.claude" agent_dir_map[gemini-cli]="$HOME/.gemini" agent_dir_map[codex]="$HOME/.codex" agent_dir_map[hermes]="$HOME/.hermes" -footer_lines=() -for agent in "${DETECTED_AGENTS[@]}"; do - base="${agent_dir_map[$agent]:-}" - [[ -z "$base" ]] && continue - while IFS= read -r skill_md; do - rel="${skill_md#${base}/skills/}" - cat_name="${rel%%/*}"; rest="${rel#*/}"; skill_name="${rest%%/*}" - ver=$(grep "^version:" "$skill_md" 2>/dev/null | head -1 | awk '{print $2}') - footer_lines+=("d:${agent}|${cat_name}|${skill_name}\t ${GRV_GRAY}${cat_name}/${skill_name} [${agent}] v${ver}${RESET}") - done < <(find "${base}/skills" -name "SKILL.md" 2>/dev/null | sort) -done - -if [[ ${#footer_lines[@]} -gt 0 ]]; then - printf "d:-\t%s\n" "$(echo -e "${GRV_PURPLE}──── Skills installés globalement ────${RESET}")" - for line in "${footer_lines[@]}"; do - printf '%s\n' "$line" +gen_repo_section() { + local active="$1" + if [[ "$active" == "1" ]]; then + printf "d:section-repo\t${GRV_PURPLE}╔══ 📦 DÉPÔT — Skills disponibles ══╗${RESET}\n" + else + printf "d:section-repo\t${GRV_GRAY}── 📦 DÉPÔT — Skills disponibles ── (TAB pour basculer)${RESET}\n" + fi + declare -A cat_map + declare -a cat_order + for i in "${!SKILLS_LIST[@]}"; do + cat="${SKILLS_LIST[$i]%%|*}" + [[ -z "${cat_map[$cat]+x}" ]] && { cat_order+=("$cat"); cat_map[$cat]=""; } + cat_map[$cat]+=" $i" done + for cat in "${cat_order[@]}"; do + indices=(${cat_map[$cat]}) + if [[ "$active" == "0" ]]; then + format_category_header "$cat" "${#indices[@]}" "1" + else + collapsed=0 + grep -qx "$cat" "$COLLAPSED_FILE" 2>/dev/null && collapsed=1 + format_category_header "$cat" "${#indices[@]}" "$collapsed" + [[ "$collapsed" == "1" ]] && continue + for idx in "${indices[@]}"; do + format_skill_line "${SKILLS_LIST[$idx]}" "$idx" + done + fi + done +} + +gen_global_section() { + local active="$1" + if [[ "$active" == "1" ]]; then + printf "d:section-global\t${GRV_PURPLE}╔══ 💾 GLOBAL — Skills installés ══╗${RESET}\n" + else + printf "d:section-global\t${GRV_GRAY}── 💾 GLOBAL — Skills installés ── (TAB pour basculer)${RESET}\n" + fi + local -a global_lines=() + for agent in "${DETECTED_AGENTS[@]}"; do + base="${agent_dir_map[$agent]:-}" + [[ -z "$base" ]] && continue + while IFS= read -r skill_md; do + rel="${skill_md#${base}/skills/}" + cat_name="${rel%%/*}"; rest="${rel#*/}"; skill_name="${rest%%/*}" + ver=$(grep "^version:" "$skill_md" 2>/dev/null | head -1 | awk '{print $2}') + global_lines+=("g:${agent}|${cat_name}|${skill_name}\t ${GRV_GRAY}${cat_name}/${skill_name}${RESET} ${GRV_GRAY}[${agent}]${RESET} ${GRV_GRAY}v${ver:-?}${RESET}") + done < <(find "${base}/skills" -name "SKILL.md" 2>/dev/null | sort) + done + if [[ ${#global_lines[@]} -eq 0 ]]; then + printf "d:-\t${GRV_GRAY} (aucun skill installé globalement)${RESET}\n" + else + for line in "${global_lines[@]}"; do + printf '%s\n' "$line" + done + fi +} + +sep() { printf "d:sep\t${GRV_GRAY}────────────────────────────────────────────────────────${RESET}\n"; } + +if [[ "$mode" == "repo" ]]; then + gen_repo_section 1 + sep + gen_global_section 0 +else + gen_global_section 1 + sep + gen_repo_section 0 fi LIST_EOF sed -i "s|FNSFILE|$fns_file|" "$list_script" @@ -556,7 +605,7 @@ elif [[ "$type" == "h" ]]; then IFS='|' read -r _ skill agent _ _ _ desc tags <<< "$entry" echo " • ${skill} [${agent}] ${desc} ${tags}" done -elif [[ "$type" == "d" && "$value" != "-" ]]; then +elif [[ "$type" == "g" || ( "$type" == "d" && "$value" != "-" && "$value" != "section-repo" && "$value" != "section-global" && "$value" != "sep" ) ]]; then IFS='|' read -r agent cat_name skill_name <<< "$value" case "$agent" in claude-code) base="$HOME/.claude" ;; @@ -629,14 +678,19 @@ $(echo -e "${GRV_BLUE}ACTIONS (SPACE pour cycler)${RESET}") $(echo -e "${GRV_BLUE}RACCOURCIS CLAVIER${RESET}") $(echo -e "${GRV_GREEN}SPACE${RESET}") Changer l'action du skill sélectionné - $(echo -e "${GRV_GREEN}TAB${RESET}") Plier / déplier la catégorie - $(echo -e "${GRV_GREEN}v${RESET}") Afficher / masquer le contenu du skill (preview) + $(echo -e "${GRV_GREEN}x${RESET}") Plier / déplier la catégorie + $(echo -e "${GRV_GREEN}TAB${RESET}") Basculer entre section DÉPÔT et section GLOBAL + $(echo -e "${GRV_GREEN}v${RESET}") Afficher / masquer le contenu du skill (preview) — ferme aussi cette aide $(echo -e "${GRV_GREEN}c${RESET}") Copier la liste affichée dans le presse-papier - $(echo -e "${GRV_GREEN}F1${RESET}") Afficher / fermer cette aide + $(echo -e "${GRV_GREEN}F1${RESET}") Ouvrir cette aide dans le panneau preview (v pour revenir au skill) + +$(echo -e "${GRV_BLUE}DEUX SECTIONS${RESET}") + $(echo -e "${GRV_PURPLE}╔══ 📦 DÉPÔT ══╗${RESET}") Section active — skills du dépôt (installables) + $(echo -e "${GRV_GRAY}── 💾 GLOBAL ──${RESET}") Section inactive — skills déjà installés $(echo -e "${GRV_BLUE}ARBRE DES CATÉGORIES${RESET}") - $(echo -e "${GRV_BLUE}▼${RESET} dev/") Catégorie dépliée — TAB pour replier - $(echo -e "${GRV_YELLOW}▶${RESET} infra/") Catégorie repliée — TAB pour déplier + $(echo -e "${GRV_BLUE}▼${RESET} dev/") Catégorie dépliée — x pour replier + $(echo -e "${GRV_YELLOW}▶${RESET} infra/") Catégorie repliée — x pour déplier $(echo -e "${GRV_GRAY}Les catégories avec >3 skills sont repliées par défaut.${RESET}") $(echo -e "${GRV_BLUE}VARIABLES D'ENVIRONNEMENT${RESET}") @@ -651,7 +705,7 @@ $(echo -e "${GRV_GRAY}Dépôt : https://gitea.maison43.duckdns.org/gilles/mes_sk HELP_EOF local legend - legend=$(echo -e "${GRV_GRAY}État: ${GRV_GREEN}✓ ${GRV_YELLOW}↑ ${GRV_AQUA}+ Action: ${GRV_GREEN}●L ${GRV_BLUE}●G ${GRV_GRAY}○ SPACE=action TAB=plier v=voir c=copier F1=aide ENTER=ok ESC=quitter${RESET}") + legend=$(echo -e "${GRV_GRAY}État: ${GRV_GREEN}✓ ${GRV_YELLOW}↑ ${GRV_AQUA}+ Action: ${GRV_GREEN}●L ${GRV_BLUE}●G ${GRV_GRAY}○ SPACE=action x=plier TAB=sections v=voir c=copier F1=aide ENTER=ok ESC=quitter${RESET}") fzf \ --ansi \ @@ -663,13 +717,14 @@ HELP_EOF --preview="bash $preview_script {1}" \ --preview-window="right:50%:wrap:hidden" \ --bind="space:execute-silent(bash $space_script {1})+reload(bash $list_script)+pos({n})" \ - --bind="tab:execute-silent(bash $tab_script {1})+reload(bash $list_script)" \ - --bind="v:toggle-preview" \ + --bind="x:execute-silent(bash $fold_script {1})+reload(bash $list_script)+pos({n})" \ + --bind="tab:execute-silent(bash $tab_script)+reload(bash $list_script)+first" \ + --bind="v:change-preview(bash $preview_script {1})+toggle-preview" \ + --bind="f1:change-preview(cat $help_file)+show-preview" \ --bind="c:execute-silent(bash $copy_script)" \ - --bind="f1:execute(less -R $help_file)" \ < <(bash "$list_script") > /dev/null || true - rm -f "$space_script" "$tab_script" "$list_script" "$fns_file" "$preview_script" "$copy_script" "$help_file" + rm -f "$space_script" "$fold_script" "$tab_script" "$list_script" "$fns_file" "$preview_script" "$copy_script" "$help_file" "$mode_file" } # ── Installation ──────────────────────────────────────────────────