From 3fde00c6ee7b9361da532f9b869a91f7d384db20 Mon Sep 17 00:00:00 2001 From: Gilles Soulier Date: Sat, 16 May 2026 04:22:47 +0200 Subject: [PATCH] =?UTF-8?q?fix:=20bugs=20install.sh=20=E2=80=94=20cleanup?= =?UTF-8?q?=20destructif,=20cl=C3=A9=20=C3=A9tat,=20tableau=20vide,=20eval?= =?UTF-8?q?=20npm=5Fprefix?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 4.6 --- install.sh | 117 +++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 87 insertions(+), 30 deletions(-) diff --git a/install.sh b/install.sh index 76cb3ca..8f2ef00 100755 --- a/install.sh +++ b/install.sh @@ -36,6 +36,7 @@ ICO_SKIP="○" # ── Configuration ───────────────────────────────────────────────── REPO_URL="https://gitea.maison43.duckdns.org/gilles/mes_skills.git" REPO_DIR="/tmp/mes_skills_$$" +_CLONED_REPO_DIR="" STATE_FILE="/tmp/skills_state_$$" SKILLS_DEBUG="${SKILLS_DEBUG:-0}" @@ -54,8 +55,8 @@ header() { echo -e "\n${GRV_PURPLE}╔══ $* ══╗${RESET}\n"; } # ── Nettoyage automatique ───────────────────────────────────────── cleanup() { - debug "Nettoyage $REPO_DIR et $STATE_FILE" - [[ -d "$REPO_DIR" ]] && rm -rf "$REPO_DIR" + debug "Nettoyage $_CLONED_REPO_DIR et $STATE_FILE" + [[ -n "$_CLONED_REPO_DIR" && -d "$_CLONED_REPO_DIR" ]] && rm -rf "$_CLONED_REPO_DIR" [[ -f "$STATE_FILE" ]] && rm -f "$STATE_FILE" } trap cleanup EXIT @@ -101,27 +102,57 @@ DETECTED_AGENTS=() detect_agents() { header "Détection des agents IA" - _check_agent() { - local name="$1" primary="$2" secondary="$3" + _add_agent() { + local name="$1" if [[ -n "$SKILLS_AGENT" && "$SKILLS_AGENT" != "$name" ]]; then debug "Agent $name ignoré (SKILLS_AGENT=$SKILLS_AGENT)" return fi - if eval "$primary" &>/dev/null 2>&1 || eval "$secondary" &>/dev/null 2>&1; then - DETECTED_AGENTS+=("$name") - ok "Agent détecté : $name" - else + DETECTED_AGENTS+=("$name") + ok "Agent détecté : $name" + } + + _skip_agent() { + local name="$1" + if [[ -z "$SKILLS_AGENT" || "$SKILLS_AGENT" == "$name" ]]; then echo -e "${GRV_GRAY}${ICO_NA} Agent absent : $name${RESET}" fi } - local npm_prefix - npm_prefix=$(npm config get prefix 2>/dev/null || echo "") + _detect_gemini() { + command -v gemini &>/dev/null && return 0 + local prefix + prefix=$(npm config get prefix 2>/dev/null) || return 1 + [[ -n "$prefix" && -f "${prefix}/bin/gemini" ]] + } - _check_agent "claude-code" "test -d $HOME/.claude" "command -v claude" - _check_agent "gemini-cli" "command -v gemini" "test -f ${npm_prefix}/bin/gemini" - _check_agent "codex" "command -v codex" "test -f $HOME/.npm-global/bin/codex" - _check_agent "hermes" "command -v hermes" "test -f $HOME/.local/bin/hermes" + # claude-code + if [[ -d "$HOME/.claude" ]] || command -v claude &>/dev/null; then + _add_agent "claude-code" + else + _skip_agent "claude-code" + fi + + # gemini-cli + if _detect_gemini; then + _add_agent "gemini-cli" + else + _skip_agent "gemini-cli" + fi + + # codex + if command -v codex &>/dev/null || [[ -f "$HOME/.npm-global/bin/codex" ]]; then + _add_agent "codex" + else + _skip_agent "codex" + fi + + # hermes + if command -v hermes &>/dev/null || [[ -f "$HOME/.local/bin/hermes" ]]; then + _add_agent "hermes" + else + _skip_agent "hermes" + fi if [[ ${#DETECTED_AGENTS[@]} -eq 0 ]]; then warn "Aucun agent IA détecté. L'installation continuera mais aucun skill ne sera filtré." @@ -138,6 +169,7 @@ clone_repo() { fi info "Clonage depuis $REPO_URL..." git clone --depth=1 "$REPO_URL" "$REPO_DIR" &>/dev/null + _CLONED_REPO_DIR="$REPO_DIR" ok "Dépôt cloné dans $REPO_DIR" } @@ -194,9 +226,11 @@ scan_skills() { # Filtre agent local agent_detected=0 - for a in "${DETECTED_AGENTS[@]:-}"; do - [[ "$a" == "$agent" ]] && agent_detected=1 - done + if [[ ${#DETECTED_AGENTS[@]} -gt 0 ]]; then + for a in "${DETECTED_AGENTS[@]}"; do + [[ "$a" == "$agent" ]] && agent_detected=1 + done + fi [[ "$agent_detected" -eq 0 && ${#DETECTED_AGENTS[@]} -gt 0 ]] && continue # Filtre tag @@ -223,11 +257,21 @@ scan_skills() { ok "${#SKILLS_LIST[@]} skill(s) trouvé(s)" } +# ── Clé d'état normalisée ──────────────────────────────────────── +make_key() { + # Entrée : "cat|skill|agent|..." — Sortie : clé normalisée pour STATE_FILE + local entry="$1" + local cat skill agent + IFS='|' read -r cat skill agent _ <<< "$entry" + # Normalise en remplaçant - et / par _ pour éviter les collisions + printf '%s_%s_%s' "${cat//-/_}" "${skill//-/_}" "${agent//-/_}" +} + # ── État du menu ────────────────────────────────────────────────── state_init() { : > "$STATE_FILE" for entry in "${SKILLS_LIST[@]}"; do - local key="${entry//|/_}" + local key; key=$(make_key "$entry") echo "${key}=local" >> "$STATE_FILE" done } @@ -255,7 +299,7 @@ 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 key; key=$(make_key "$entry") local action; action=$(state_get "$key") local ico_etat color_etat @@ -296,23 +340,34 @@ run_menu() { 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) +SKILLS_FNS="$2" +LINE_NUM="$3" # numéro de ligne fzf (1-based) + +source "$SKILLS_FNS" + +# Récupère l'entrée correspondante (0-based dans SKILLS_LIST) +idx=$(( LINE_NUM - 1 )) +entry="${SKILLS_LIST[$idx]:-}" +[[ -z "$entry" ]] && exit 0 + +key=$(make_key "$entry") +etat=$(echo "$entry" | cut -d'|' -f4) + +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" ;; + skip) [[ "$etat" == "upd" ]] && next="update" || next="local" ;; update) next="local" ;; *) next="local" ;; esac -sed -i "s|^${KEY}=.*|${KEY}=${next}|" "$STATE_FILE" +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 -f format_skill_line state_get make_key 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'" @@ -336,7 +391,7 @@ LIST_EOF --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)" \ + --bind="tab:execute-silent($cycle_script '$STATE_FILE' '$fns_file' {n})+reload($list_script)" \ < <(bash "$list_script") > /dev/null || true rm -f "$cycle_script" "$list_script" "$fns_file" @@ -350,7 +405,7 @@ install_selected() { 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 key; key=$(make_key "$entry") local action; action=$(state_get "$key") if [[ "$action" == "skip" ]]; then @@ -396,12 +451,14 @@ print_summary() { 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 key; key=$(make_key "$entry") local action; action=$(state_get "$key") [[ "$action" == "skip" ]] && continue local already=0 - for s in "${shown[@]:-}"; do [[ "$s" == "${skill}|${agent}" ]] && already=1; done + if [[ ${#shown[@]} -gt 0 ]]; then + for s in "${shown[@]}"; do [[ "$s" == "${skill}|${agent}" ]] && already=1; done + fi [[ "$already" -eq 1 ]] && continue shown+=("${skill}|${agent}") @@ -414,7 +471,7 @@ print_summary() { done echo -e "\n${GRV_PURPLE}╔══ Documentation agents ══╗${RESET}\n" - for agent in "${DETECTED_AGENTS[@]:-}"; do + for agent in "${DETECTED_AGENTS[@]}"; do case "$agent" in claude-code) echo -e " ${GRV_BLUE}Claude Code${RESET} → https://code.claude.com/docs/en/skills" ;; gemini-cli) echo -e " ${GRV_BLUE}Gemini CLI ${RESET} → https://github.com/google-gemini/gemini-cli/blob/main/docs/cli/skills.md" ;;