# Dépôt Skills IA + Installeur Bash — Plan d'implémentation > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Créer la structure complète du dépôt de skills multi-agents et le script `install.sh` interactif (fzf + couleurs Gruvbox). **Architecture:** Dépôt Gitea avec skills organisés par catégorie puis par agent. Script bash autonome avec menu fzf, état par skill dans un fichier temporaire, palette Gruvbox Dark 256 couleurs. Aucun sudo requis, zéro dépendance hors git+fzf. **Tech Stack:** Bash 5+, fzf, git, sort -V (semver), ANSI 256 couleurs. **Spec de référence:** `docs/superpowers/specs/2026-05-16-mes-skills-design.md` --- ## Fichiers à créer / modifier | Fichier | Rôle | |---------|------| | `skills/dev/debugging/claude-code.md` | Skill démo Claude Code | | `skills/dev/debugging/gemini-cli.md` | Skill démo Gemini CLI | | `skills/dev/debugging/codex.md` | Skill démo Codex | | `skills/dev/debugging/hermes.md` | Skill démo Hermes | | `templates/claude-code.md` | Squelette vierge Claude Code | | `templates/gemini-cli.md` | Squelette vierge Gemini CLI | | `templates/codex.md` | Squelette vierge Codex | | `templates/hermes.md` | Squelette vierge Hermes | | `web/.gitkeep` | Réserve le dossier web pour le plan 2 | | `docs/structure_skill.md` | Référence format SKILL.md par agent | | `docs/structure_repo.md` | Référence structure bibliothèque | | `docs/structure_script_install.md` | Référence architecture install.sh | | `README.md` | Guide d'utilisation | | `evolution.md` | Améliorations prévues | | `install.sh` | Script principal (exécutable) | | `tests/test_install.sh` | Tests bash (assertions simples) | --- ## Task 1 : Structure des dossiers et templates **Files:** - Create: `skills/dev/debugging/claude-code.md` - Create: `skills/dev/debugging/gemini-cli.md` - Create: `skills/dev/debugging/codex.md` - Create: `skills/dev/debugging/hermes.md` - Create: `templates/claude-code.md` - Create: `templates/gemini-cli.md` - Create: `templates/codex.md` - Create: `templates/hermes.md` - Create: `web/.gitkeep` - [ ] **Étape 1 : Créer l'arborescence des dossiers** ```bash mkdir -p skills/{dev,infra,ai,tools,jardinage,electronique,diy,task} mkdir -p skills/dev/debugging mkdir -p templates mkdir -p web touch web/.gitkeep mkdir -p tests ``` - [ ] **Étape 2 : Créer le template Claude Code** Créer `templates/claude-code.md` : ```markdown --- name: mon-skill version: 1.0.0 description: Décris clairement ce que fait ce skill et quand il se déclenche. agents: [claude-code] category: dev tags: [tag1, tag2] --- # Mon Skill ## Objectif Décris l'objectif du skill en une phrase. ## Quand l'utiliser - Cas d'usage 1 - Cas d'usage 2 ## Instructions Écris ici les instructions que Claude Code doit suivre. ``` - [ ] **Étape 3 : Créer le template Gemini CLI** Créer `templates/gemini-cli.md` : ```markdown --- name: mon-skill version: 1.0.0 description: Décris clairement ce que fait ce skill et quand il se déclenche. agents: [gemini-cli] category: dev tags: [tag1, tag2] --- # Mon Skill ## Objectif Décris l'objectif du skill en une phrase. ## Quand l'utiliser - Cas d'usage 1 - Cas d'usage 2 ## Instructions Écris ici les instructions que Gemini CLI doit suivre. ``` - [ ] **Étape 4 : Créer le template Codex** Créer `templates/codex.md` : ```markdown --- name: mon-skill version: 1.0.0 description: Décris clairement ce que fait ce skill et quand il se déclenche. allow_implicit_invocation: true agents: [codex] category: dev tags: [tag1, tag2] --- # Mon Skill ## Objectif Décris l'objectif du skill en une phrase. ## Quand l'utiliser - Cas d'usage 1 - Cas d'usage 2 ## Instructions Écris ici les instructions que Codex doit suivre. ``` - [ ] **Étape 5 : Créer le template Hermes** Créer `templates/hermes.md` : ```markdown --- name: mon-skill version: 1.0.0 description: Décris clairement ce que fait ce skill et quand il se déclenche. agents: [hermes] category: dev tags: [tag1, tag2] metadata: hermes: tags: [tag1, tag2] category: dev --- # Mon Skill ## Quand utiliser ce skill - Cas d'usage 1 ## Référence rapide | Élément | Valeur | |---------|--------| | Commande | `exemple` | ## Procédure 1. Étape 1 2. Étape 2 ## Pièges connus - Piège 1 ## Vérification - [ ] Résultat attendu 1 ``` - [ ] **Étape 6 : Créer le skill de démo `debugging` pour Claude Code** Créer `skills/dev/debugging/claude-code.md` : ```markdown --- name: debugging version: 1.0.0 description: Systématise le processus de débogage. Utiliser quand un bug est détecté, un test échoue ou un comportement inattendu est observé. agents: [claude-code] category: dev tags: [debug, test, bug] --- # Debugging Systématique ## Objectif Fournir une approche structurée et reproductible pour identifier et corriger les bugs. ## Quand l'utiliser - Un test échoue - Un comportement inattendu est observé - Une erreur apparaît dans les logs ## Instructions 1. **Reproduire** : Isoler le cas minimal qui déclenche le bug 2. **Observer** : Lire le message d'erreur complet sans supposer 3. **Hypothèse** : Formuler une seule hypothèse à la fois 4. **Tester** : Vérifier l'hypothèse avec une modification minimale 5. **Corriger** : Appliquer la correction et vérifier que le test passe 6. **Régression** : S'assurer qu'aucun autre test n'est cassé ``` - [ ] **Étape 7 : Créer le skill démo pour Gemini CLI, Codex, Hermes** Créer `skills/dev/debugging/gemini-cli.md` : ```markdown --- name: debugging version: 1.0.0 description: Systématise le débogage. Utiliser quand un bug ou comportement inattendu est observé. agents: [gemini-cli] category: dev tags: [debug, test, bug] --- # Debugging Systématique ## Instructions 1. Reproduire le bug avec le cas minimal 2. Lire le message d'erreur complet 3. Formuler une hypothèse unique 4. Tester et corriger 5. Vérifier les régressions ``` Créer `skills/dev/debugging/codex.md` : ```markdown --- name: debugging version: 1.0.0 description: Systématise le débogage. Utiliser quand un bug ou comportement inattendu est observé. allow_implicit_invocation: true agents: [codex] category: dev tags: [debug, test, bug] --- # Debugging Systématique ## Instructions 1. Reproduire le bug avec le cas minimal 2. Lire le message d'erreur complet 3. Formuler une hypothèse unique 4. Tester et corriger 5. Vérifier les régressions ``` Créer `skills/dev/debugging/hermes.md` : ```markdown --- name: debugging version: 1.0.0 description: Systématise le débogage. Utiliser quand un bug ou comportement inattendu est observé. agents: [hermes] category: dev tags: [debug, test, bug] metadata: hermes: tags: [debug, test, bug] category: dev --- # Debugging Systématique ## Quand utiliser ce skill - Un test échoue - Un comportement inattendu est observé ## Procédure 1. Reproduire le bug avec le cas minimal 2. Lire le message d'erreur complet 3. Formuler une hypothèse unique 4. Tester et corriger 5. Vérifier les régressions ## Vérification - [ ] Le bug est reproduit avant correction - [ ] La correction ne casse aucun autre test ``` - [ ] **Étape 8 : Commit** ```bash rtk git add skills/ templates/ web/.gitkeep rtk git commit -m "feat: structure dossiers, templates et skill démo debugging" ``` --- ## Task 2 : Documentation — `docs/structure_skill.md` **Files:** - Create: `docs/structure_skill.md` - [ ] **Étape 1 : Créer `docs/structure_skill.md`** ```markdown # Structure des Skills par Agent IA Ce document décrit le format exact attendu pour chaque agent. Un même skill peut exister en plusieurs versions (une par agent). --- ## Format commun (frontmatter YAML) Tous les agents partagent ces champs de base : | Champ | Requis | Description | |-------|--------|-------------| | `name` | oui | Identifiant unique du skill (kebab-case) | | `version` | oui | Version semver `X.Y.Z` | | `description` | oui | Description + déclencheurs (crucial pour l'activation automatique) | | `agents` | oui | Liste des agents : `[claude-code]`, `[gemini-cli]`, etc. | | `category` | oui | Catégorie : `dev`, `infra`, `ai`, `tools`, `jardinage`, `electronique`, `diy`, `task` | | `tags` | recommandé | Mots-clés libres : `[bash, debug, git]` | --- ## Claude Code **Docs :** https://code.claude.com/docs/en/skills **Fichier dans le dépôt :** `skills///claude-code.md` **Destination globale :** `~/.claude/skills///SKILL.md` **Destination locale :** `.claude/skills///SKILL.md` ```yaml --- name: mon-skill version: 1.0.0 description: Description et déclencheurs. agents: [claude-code] category: dev tags: [tag1, tag2] # Optionnels : # disable-model-invocation: true # seul l'utilisateur peut invoquer # user-invocable: false # seul Claude peut invoquer # allowed-tools: [Read, Grep] # restreint les outils disponibles --- ``` **Commande de test :** ```bash claude "utilise le skill " --print ``` --- ## Gemini CLI **Docs :** https://github.com/google-gemini/gemini-cli/blob/main/docs/cli/skills.md **Fichier dans le dépôt :** `skills///gemini-cli.md` **Destination globale :** `~/.gemini/skills///SKILL.md` **Destination locale :** `.gemini/skills///SKILL.md` **Alias :** `~/.agents/skills/` et `.agents/skills/` ```yaml --- name: mon-skill version: 1.0.0 description: Description et déclencheurs. agents: [gemini-cli] category: dev tags: [tag1, tag2] --- ``` **Commande de test :** ```bash gemini -p "utilise le skill " ``` --- ## Codex (OpenAI) **Docs :** https://developers.openai.com/codex/skills **Fichier dans le dépôt :** `skills///codex.md` **Destination globale :** `~/.codex/skills///SKILL.md` **Destination locale :** `.codex/skills///SKILL.md` ```yaml --- name: mon-skill version: 1.0.0 description: Description et déclencheurs. allow_implicit_invocation: true agents: [codex] category: dev tags: [tag1, tag2] --- ``` **Commande de test :** ```bash codex "$" ``` --- ## Hermes Agent (NousResearch) **Docs :** https://hermes-agent.nousresearch.com/docs/user-guide/features/skills/ **Fichier dans le dépôt :** `skills///hermes.md` **Destination globale :** `~/.hermes/skills///SKILL.md` **Destination locale :** `.hermes/skills///SKILL.md` ```yaml --- name: mon-skill version: 1.0.0 description: Description et déclencheurs. agents: [hermes] category: dev tags: [tag1, tag2] metadata: hermes: tags: [tag1, tag2] category: dev --- ``` Structure Markdown recommandée par Hermes : - `## Quand utiliser ce skill` - `## Référence rapide` - `## Procédure` - `## Pièges connus` - `## Vérification` **Commande de test :** ```bash hermes "utilise le skill " ``` ``` - [ ] **Étape 2 : Commit** ```bash rtk git add docs/structure_skill.md rtk git commit -m "docs: référence format SKILL.md par agent" ``` --- ## Task 3 : Documentation — `docs/structure_repo.md` et `docs/structure_script_install.md` **Files:** - Create: `docs/structure_repo.md` - Create: `docs/structure_script_install.md` - [ ] **Étape 1 : Créer `docs/structure_repo.md`** ```markdown # Structure de la Bibliothèque de Skills ## Arborescence ``` mes_skills/ ├── skills/ ← bibliothèque principale │ ├── / ← catégorie (dev, infra, jardinage, ...) │ │ └── / ← un dossier par skill │ │ ├── claude-code.md ← variante Claude Code │ │ ├── gemini-cli.md ← variante Gemini CLI │ │ ├── codex.md ← variante Codex │ │ └── hermes.md ← variante Hermes Agent ├── templates/ ← squelettes vierges par agent │ ├── claude-code.md │ ├── gemini-cli.md │ ├── codex.md │ └── hermes.md ├── web/ ← portail web statique (Hugo + Docker) ├── docs/ ← documentation du dépôt ├── tests/ ← tests bash de install.sh ├── install.sh ← installeur interactif ├── README.md ├── evolution.md └── CLAUDE.md ``` ## Catégories disponibles | Catégorie | Usage | |-----------|-------| | `dev` | Développement : debug, git, tests, refactoring | | `infra` | Infrastructure : docker, proxmox, homelab | | `ai` | Agents IA : prompting, workflows | | `tools` | Scripts utilitaires, automatisations | | `jardinage` | Jardinage et plantes | | `electronique` | Électronique, Arduino, ESP32 | | `diy` | Bricolage, impression 3D | | `task` | Gestion de tâches, organisation | ## Conventions - Nom du skill : kebab-case (`mon-skill`) - Nom des fichiers : `.md` (ex: `claude-code.md`) - Version : semver `X.Y.Z` dans le frontmatter - Tags : minuscules, sans espace (ex: `[bash, debug]`) - L'installeur copie `.md` → `SKILL.md` dans la destination ## Ajouter un nouveau skill 1. Créer le dossier : `skills///` 2. Copier un template depuis `templates/.md` 3. Remplir le frontmatter et le contenu 4. Committer et pusher vers Gitea ``` - [ ] **Étape 2 : Créer `docs/structure_script_install.md`** ```markdown # Architecture de install.sh ## Vue d'ensemble `install.sh` est un script bash autonome qui : 1. Vérifie les dépendances (git, fzf) 2. Détecte les agents IA installés 3. Clone le dépôt en /tmp 4. Présente un menu fzf interactif avec état par skill 5. Installe les skills sélectionnés sans sudo 6. Affiche un bilan + commandes de test ## Variables d'environnement | Variable | Défaut | Effet | |----------|--------|-------| | `SKILLS_DEBUG=1` | non | Affiche chaque étape en détail | | `SKILLS_DRY_RUN=1` | non | Simule sans écrire de fichiers | | `SKILLS_REPO=/path` | (Gitea) | Utilise un dépôt local | | `SKILLS_TAG=bash` | (tous) | Filtre les skills par tag | | `SKILLS_AGENT=claude` | (auto) | Force un seul agent | ## Palette Gruvbox Dark (256 couleurs) | Variable | Code | Usage | |----------|------|-------| | `GRV_GREEN` | `\033[38;5;142m` | Succès, installé ✓ | | `GRV_YELLOW` | `\033[38;5;214m` | MAJ dispo ↑ | | `GRV_AQUA` | `\033[38;5;108m` | Nouveau skill + | | `GRV_GRAY` | `\033[38;5;245m` | Non applicable · | | `GRV_RED` | `\033[38;5;167m` | Erreur fatale | | `GRV_ORANGE` | `\033[38;5;208m` | Avertissement | | `GRV_BLUE` | `\033[38;5;109m` | Info, action globale | | `GRV_PURPLE` | `\033[38;5;175m` | Encadrés, titres | ## Flux d'exécution ``` check_deps() └─ fzf absent → install_fzf() ou exit └─ git absent → exit 1 detect_agents() → tableau DETECTED_AGENTS[] clone_repo() → /tmp/mes_skills_/ scan_skills() └─ pour chaque skills///.md du dépôt └─ si agent dans DETECTED_AGENTS └─ get_local_version() vs get_repo_version() └─ compare_versions() → état (✓ ↑ + ·) run_menu() [fzf + fichier état /tmp/skills_state_] └─ TAB → cycle_action() + reload └─ ENTER → liste des sélections install_selected() └─ mkdir -p destination └─ cp .md → SKILL.md print_summary() └─ bilan coloré └─ commandes de test copiables └─ liens docs agents cleanup() [trap EXIT] ``` ## Destinations d'installation (sans sudo) | Agent | Global | Local | |-------|--------|-------| | claude-code | `~/.claude/skills///SKILL.md` | `.claude/skills///SKILL.md` | | gemini-cli | `~/.gemini/skills///SKILL.md` | `.gemini/skills///SKILL.md` | | codex | `~/.codex/skills///SKILL.md` | `.codex/skills///SKILL.md` | | hermes | `~/.hermes/skills///SKILL.md` | `.hermes/skills///SKILL.md` | ``` - [ ] **Étape 3 : Commit** ```bash rtk git add docs/structure_repo.md docs/structure_script_install.md rtk git commit -m "docs: structure dépôt et architecture install.sh" ``` --- ## Task 4 : `README.md` et `evolution.md` **Files:** - Create: `README.md` - Create: `evolution.md` - [ ] **Étape 1 : Créer `README.md`** ```markdown # mes_skills — Bibliothèque personnelle de skills IA Dépôt Gitea personnel de skills réutilisables pour agents IA : **Claude Code**, **Gemini CLI**, **Codex (OpenAI)**, **Hermes Agent**. ## Installation rapide ```bash curl -fsSL https://gitea.maison43.duckdns.org/gilles/mes_skills/raw/branch/main/install.sh | bash ``` ## Ce que fait l'installeur 1. Détecte les agents IA présents sur ton système 2. Affiche la liste des skills disponibles avec leur statut (installé / nouveau / MAJ dispo) 3. Menu interactif fzf : TAB pour choisir local/global/ignorer, ENTER pour confirmer 4. Installe sans sudo dans `~/.claude/skills/`, `~/.gemini/skills/`, etc. 5. Affiche les commandes pour tester chaque skill installé ## Options avancées ```bash SKILLS_DEBUG=1 bash install.sh # mode verbeux SKILLS_DRY_RUN=1 bash install.sh # simulation sans écriture SKILLS_TAG=bash bash install.sh # filtre par tag SKILLS_REPO=/path bash install.sh # dépôt local (sans clone) ``` ## Structure du dépôt ``` skills/// claude-code.md ← skill pour Claude Code gemini-cli.md ← skill pour Gemini CLI codex.md ← skill pour Codex hermes.md ← skill pour Hermes ``` Voir [docs/structure_repo.md](docs/structure_repo.md) pour le détail complet. ## Créer un nouveau skill ```bash cp templates/claude-code.md skills/dev/mon-skill/claude-code.md # Éditer le fichier, remplir name, version, description, tags # Committer et pusher ``` ## Documentation - [Format des skills par agent](docs/structure_skill.md) - [Structure du dépôt](docs/structure_repo.md) - [Architecture de install.sh](docs/structure_script_install.md) - [Évolutions prévues](evolution.md) ``` - [ ] **Étape 2 : Créer `evolution.md`** ```markdown # Évolutions et Améliorations Prévues ## v2 — Site web statique (en cours de conception) - Portail Hugo + Docker dans `web/` - Navigation par catégorie, agent et tags - Style CSS Gruvbox dark - Synchronisation automatique avec le dépôt Gitea (git pull toutes les heures) - Voir `docs/superpowers/plans/` pour le plan d'implémentation ## v2.1 — Éditeur de skills depuis le navigateur - Éditeur Markdown intégré au portail web - Création depuis template - Push vers Gitea via token API ## Futures idées - Upload de skills locaux vers Gitea (token API) - Support des dossiers `scripts/` et `references/` (format Hermes complet) - Signature/vérification d'intégrité des skills - Synchronisation automatique via cron système - Interface similaire à [skillsmp.com](https://skillsmp.com/fr) ``` - [ ] **Étape 3 : Commit** ```bash rtk git add README.md evolution.md rtk git commit -m "docs: README et evolution.md" ``` --- ## Task 5 : `install.sh` — socle, couleurs et helpers **Files:** - Create: `install.sh` - [ ] **Étape 1 : Créer `install.sh` avec en-tête et palette Gruvbox** Créer `install.sh` : ```bash #!/usr/bin/env bash # install.sh — Installeur interactif de skills IA # Dépôt : https://gitea.maison43.duckdns.org/gilles/mes_skills set -euo pipefail # ── Couleurs Gruvbox Dark 256 ────────────────────────────────────── GRV_FG='\033[38;5;223m' GRV_RED='\033[38;5;167m' GRV_GREEN='\033[38;5;142m' GRV_YELLOW='\033[38;5;214m' GRV_BLUE='\033[38;5;109m' GRV_PURPLE='\033[38;5;175m' GRV_AQUA='\033[38;5;108m' GRV_ORANGE='\033[38;5;208m' GRV_GRAY='\033[38;5;245m' RESET='\033[0m' # ── Icônes ──────────────────────────────────────────────────────── ICO_OK="✓" ICO_UPD="↑" ICO_NEW="+" ICO_NA="·" ICO_LOCAL="●L" ICO_GLOBAL="●G" ICO_SKIP="○" # ── Configuration ───────────────────────────────────────────────── REPO_URL="https://gitea.maison43.duckdns.org/gilles/mes_skills.git" REPO_DIR="/tmp/mes_skills_$$" STATE_FILE="/tmp/skills_state_$$" SKILLS_DEBUG="${SKILLS_DEBUG:-0}" SKILLS_DRY_RUN="${SKILLS_DRY_RUN:-0}" SKILLS_REPO="${SKILLS_REPO:-}" SKILLS_TAG="${SKILLS_TAG:-}" SKILLS_AGENT="${SKILLS_AGENT:-}" # ── Helpers couleur ─────────────────────────────────────────────── ok() { echo -e "${GRV_GREEN}${ICO_OK} $*${RESET}"; } err() { echo -e "${GRV_RED}✗ $*${RESET}" >&2; } info() { echo -e "${GRV_BLUE}→ $*${RESET}"; } warn() { echo -e "${GRV_ORANGE}⚠ $*${RESET}"; } debug() { [[ "$SKILLS_DEBUG" == "1" ]] && echo -e "${GRV_GRAY}[DBG] $*${RESET}" || true; } header() { echo -e "\n${GRV_PURPLE}╔══ $* ══╗${RESET}\n"; } ``` - [ ] **Étape 2 : Ajouter la fonction `cleanup` (trap EXIT)** Ajouter à la suite dans `install.sh` : ```bash # ── Nettoyage automatique ───────────────────────────────────────── cleanup() { debug "Nettoyage $REPO_DIR et $STATE_FILE" [[ -d "$REPO_DIR" ]] && rm -rf "$REPO_DIR" [[ -f "$STATE_FILE" ]] && rm -f "$STATE_FILE" } trap cleanup EXIT ``` - [ ] **Étape 3 : Commit intermédiaire** ```bash chmod +x install.sh rtk git add install.sh rtk git commit -m "feat: install.sh socle — palette Gruvbox et helpers" ``` --- ## Task 6 : `install.sh` — vérification des dépendances et détection agents **Files:** - Modify: `install.sh` - [ ] **Étape 1 : Ajouter `install_fzf()`** Ajouter dans `install.sh` après `cleanup` : ```bash # ── Installation fzf ────────────────────────────────────────────── install_fzf() { warn "fzf non trouvé." echo -e " ${GRV_FG}Installer fzf ? [o/N]${RESET} \c" read -r answer [[ "$answer" != "o" && "$answer" != "O" ]] && err "fzf requis. Abandon." && exit 1 if command -v apt-get &>/dev/null; then debug "Installation via apt" apt-get install -y fzf 2>/dev/null || { warn "apt échoué, tentative binaire GitHub..." _install_fzf_binary } else _install_fzf_binary fi command -v fzf &>/dev/null && ok "fzf installé." || { err "Impossible d'installer fzf."; exit 1; } } _install_fzf_binary() { local fzf_url="https://github.com/junegunn/fzf/releases/latest/download/fzf-linux_amd64.tar.gz" local tmp_fzf="/tmp/fzf_$$.tar.gz" info "Téléchargement fzf depuis GitHub Releases..." curl -fsSL "$fzf_url" -o "$tmp_fzf" tar -xzf "$tmp_fzf" -C "$HOME/.local/bin/" fzf rm -f "$tmp_fzf" export PATH="$HOME/.local/bin:$PATH" } ``` - [ ] **Étape 2 : Ajouter `check_deps()`** ```bash # ── Vérification des dépendances ────────────────────────────────── check_deps() { header "Vérification des dépendances" command -v git &>/dev/null || { err "git non trouvé. Installer git et relancer."; exit 1; } ok "git $(git --version | awk '{print $3}')" command -v fzf &>/dev/null || install_fzf ok "fzf $(fzf --version | awk '{print $1}')" } ``` - [ ] **Étape 3 : Ajouter `detect_agents()`** ```bash # ── Détection des agents IA ─────────────────────────────────────── DETECTED_AGENTS=() detect_agents() { header "Détection des agents IA" local check_agent() { local name="$1" primary="$2" secondary="$3" if [[ -n "$SKILLS_AGENT" && "$SKILLS_AGENT" != "$name" ]]; then debug "Agent $name ignoré (SKILLS_AGENT=$SKILLS_AGENT)" return fi if eval "$primary" &>/dev/null || eval "$secondary" &>/dev/null 2>/dev/null; then DETECTED_AGENTS+=("$name") ok "Agent détecté : $name" else echo -e "${GRV_GRAY}${ICO_NA} Agent absent : $name${RESET}" fi } check_agent "claude-code" "test -d $HOME/.claude" "command -v claude" check_agent "gemini-cli" "command -v gemini" "test -f $(npm config get prefix 2>/dev/null)/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" if [[ ${#DETECTED_AGENTS[@]} -eq 0 ]]; then warn "Aucun agent IA détecté. L'installation continuera mais aucun skill ne sera filtré." fi } ``` - [ ] **Étape 4 : Commit** ```bash rtk git add install.sh rtk git commit -m "feat: install.sh — détection dépendances et agents IA" ``` --- ## Task 7 : `install.sh` — clone, scan skills et versions **Files:** - Modify: `install.sh` - [ ] **Étape 1 : Ajouter `clone_repo()`** ```bash # ── Clone du dépôt ──────────────────────────────────────────────── clone_repo() { header "Récupération du dépôt" if [[ -n "$SKILLS_REPO" ]]; then REPO_DIR="$SKILLS_REPO" info "Utilisation du dépôt local : $REPO_DIR" return fi info "Clonage depuis $REPO_URL..." git clone --depth=1 "$REPO_URL" "$REPO_DIR" &>/dev/null ok "Dépôt cloné dans $REPO_DIR" } ``` - [ ] **Étape 2 : Ajouter les fonctions de parsing et comparaison versions** ```bash # ── Gestion des versions ────────────────────────────────────────── get_frontmatter_field() { local file="$1" field="$2" grep "^${field}:" "$file" 2>/dev/null | head -1 | awk '{print $2}' | tr -d '"'\' } get_local_version() { local cat="$1" skill="$2" agent="$3" local dest dest=$(get_dest_path "$cat" "$skill" "$agent" "local") [[ -f "$dest" ]] && get_frontmatter_field "$dest" "version" || echo "" } # Retourne 1 si ver1 < ver2 (ver2 est plus récente) version_is_newer() { local ver1="$1" ver2="$2" [[ "$ver1" == "$ver2" ]] && return 1 local newest newest=$(printf '%s\n%s' "$ver1" "$ver2" | sort -V | tail -1) [[ "$newest" == "$ver2" ]] } ``` - [ ] **Étape 3 : Ajouter `get_dest_path()` et `scan_skills()`** ```bash # ── Chemins de destination ──────────────────────────────────────── get_dest_path() { local cat="$1" skill="$2" agent="$3" scope="$4" local base case "$agent" in claude-code) base="${scope//local/.claude}"; [[ "$scope" == "global" ]] && base="$HOME/.claude" ;; gemini-cli) [[ "$scope" == "global" ]] && base="$HOME/.gemini" || base=".gemini" ;; codex) [[ "$scope" == "global" ]] && base="$HOME/.codex" || base=".codex" ;; hermes) [[ "$scope" == "global" ]] && base="$HOME/.hermes" || base=".hermes" ;; esac echo "${base}/skills/${cat}/${skill}/SKILL.md" } # ── Scan des skills disponibles ─────────────────────────────────── # Format interne : "cat|skill|agent|etat|repo_version|local_version" SKILLS_LIST=() scan_skills() { header "Scan des skills disponibles" SKILLS_LIST=() while IFS= read -r skill_file; do local rel="${skill_file#$REPO_DIR/skills/}" # dev/debugging/claude-code.md local agent_file="${rel##*/}" # claude-code.md local agent="${agent_file%.md}" # claude-code local skill_path="${rel%/*}" # dev/debugging local cat="${skill_path%%/*}" # dev local skill="${skill_path#*/}" # debugging # Filtre agent local agent_detected=0 for a in "${DETECTED_AGENTS[@]:-}"; do [[ "$a" == "$agent" ]] && agent_detected=1; done [[ "$agent_detected" -eq 0 && ${#DETECTED_AGENTS[@]} -gt 0 ]] && continue # Filtre tag if [[ -n "$SKILLS_TAG" ]]; then grep -q "$SKILLS_TAG" "$skill_file" || continue fi local repo_ver; repo_ver=$(get_frontmatter_field "$skill_file" "version") local local_ver; local_ver=$(get_local_version "$cat" "$skill" "$agent") local etat if [[ -z "$local_ver" ]]; then etat="new" elif version_is_newer "$local_ver" "$repo_ver"; then etat="upd" else etat="ok" fi SKILLS_LIST+=("${cat}|${skill}|${agent}|${etat}|${repo_ver}|${local_ver}") debug "Skill trouvé : $cat/$skill [$agent] état=$etat" done < <(find "$REPO_DIR/skills" -name "*.md" | sort) ok "${#SKILLS_LIST[@]} skill(s) trouvé(s)" } ``` - [ ] **Étape 4 : Commit** ```bash rtk git add install.sh rtk git commit -m "feat: install.sh — clone dépôt, scan skills et comparaison versions" ``` --- ## Task 8 : `install.sh` — menu fzf et installation **Files:** - Modify: `install.sh` - [ ] **Étape 1 : Configurer le thème fzf Gruvbox** Ajouter dans `install.sh` après les variables de config : ```bash # ── Thème fzf Gruvbox Dark ──────────────────────────────────────── export FZF_DEFAULT_OPTS=" --color=bg+:#3c3836,bg:#282828,spinner:#fb4934,hl:#928374 --color=fg:#ebdbb2,header:#928374,info:#8ec07c,pointer:#fb4934 --color=marker:#fb4934,fg+:#ebdbb2,prompt:#fb4934,hl+:#fb4934 --border=rounded --height=80% --layout=reverse --header-lines=2 " ``` - [ ] **Étape 2 : Ajouter les fonctions de gestion d'état par skill** ```bash # ── État du menu (fichier clé=valeur) ──────────────────────────── state_init() { > "$STATE_FILE" for entry in "${SKILLS_LIST[@]}"; do local key="${entry//|/_}" local etat; etat=$(echo "$entry" | cut -d'|' -f4) # Défaut : local (sauf si etat=ok → local aussi mais MAJ si upd) echo "${key}=local" >> "$STATE_FILE" done } state_get() { grep "^${1}=" "$STATE_FILE" 2>/dev/null | cut -d'=' -f2; } state_cycle() { local key="$1" local etat="$2" # ok|new|upd local current; current=$(state_get "$key") local next case "$current" in local) next="global" ;; global) next="skip" ;; skip) if [[ "$etat" == "upd" ]]; then next="update" else next="local"; fi ;; update) next="local" ;; *) next="local" ;; esac sed -i "s/^${key}=.*/${key}=${next}/" "$STATE_FILE" } ``` - [ ] **Étape 3 : Ajouter `format_skill_line()` et `run_menu()`** ```bash # ── Formatage d'une ligne du menu ───────────────────────────────── format_skill_line() { local entry="$1" IFS='|' read -r cat skill agent etat repo_ver local_ver <<< "$entry" local key="${cat}_${skill}_${agent}" local action; action=$(state_get "$key") # Icône état 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 # Icône action 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" ;; esac local ver_info="" [[ "$etat" == "upd" ]] && ver_info=" (${local_ver}→${repo_ver})" [[ "$etat" == "new" ]] && ver_info=" (v${repo_ver})" printf "${color_etat}%s${RESET} %-30s ${GRV_GRAY}[%s]${RESET} ${color_action}%s${RESET}%s\n" \ "$ico_etat" "${cat}/${skill}" "$agent" "$ico_action" "$ver_info" } # ── Menu fzf principal ──────────────────────────────────────────── SELECTED_SKILLS=() run_menu() { header "Sélection des skills" state_init # Fichier helper pour le cycle TAB (appelé par fzf) local cycle_script="/tmp/skills_cycle_$$.sh" cat > "$cycle_script" << 'CYCLE_EOF' #!/usr/bin/env bash STATE_FILE="$1" KEY="$2" ETAT="$3" current=$(grep "^${KEY}=" "$STATE_FILE" | 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" # Generateur de liste pour fzf local list_script="/tmp/skills_list_$$.sh" cat > "$list_script" << LIST_EOF #!/usr/bin/env bash source /tmp/skills_fns_$$.sh for entry in $(printf '"%s" ' "${SKILLS_LIST[@]}"); do format_skill_line "\$entry" done LIST_EOF # Exporter les fonctions nécessaires declare -f format_skill_line state_get > "/tmp/skills_fns_$$.sh" echo "STATE_FILE='$STATE_FILE'" >> "/tmp/skills_fns_$$.sh" # Variables icônes et couleurs declare -p GRV_GREEN GRV_YELLOW GRV_AQUA GRV_GRAY GRV_BLUE GRV_RED GRV_PURPLE RESET \ ICO_OK ICO_UPD ICO_NEW ICO_NA ICO_LOCAL ICO_GLOBAL ICO_SKIP >> "/tmp/skills_fns_$$.sh" chmod +x "$list_script" local legend legend="$(echo -e "${GRV_GRAY}État: ${GRV_GREEN}✓ installé ${GRV_YELLOW}↑ MAJ ${GRV_AQUA}+ nouveau ${GRV_GRAY}· n/a Action: ${GRV_GREEN}●L local ${GRV_BLUE}●G global ${GRV_GRAY}○ ignorer TAB=changer ENTER=ok${RESET}")" fzf \ --ansi \ --multi \ --prompt="Skills > " \ --header="$legend" \ --bind="tab:execute-silent($cycle_script '$STATE_FILE' {1} {4})+reload($list_script)" \ < <(bash "$list_script") | while IFS= read -r line; do SELECTED_SKILLS+=("$line") done rm -f "$cycle_script" "$list_script" "/tmp/skills_fns_$$.sh" } ``` Note : L'approche `execute-silent+reload` permet le cycle d'état en temps réel dans fzf. - [ ] **Étape 4 : Ajouter `install_selected()`** ```bash # ── Installation des skills sélectionnés ───────────────────────── install_selected() { header "Installation" local count_install=0 count_update=0 count_skip=0 for entry in "${SKILLS_LIST[@]}"; do IFS='|' read -r cat skill agent etat repo_ver local_ver <<< "$entry" local key="${cat}_${skill}_${agent}" local action; action=$(state_get "$key") [[ "$action" == "skip" ]] && (( count_skip++ )) && continue local src="${REPO_DIR}/skills/${cat}/${skill}/${agent}.md" local dest; dest=$(get_dest_path "$cat" "$skill" "$agent" "$action") [[ "$action" == "update" ]] && dest=$(get_dest_path "$cat" "$skill" "$agent" "local") 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++ )) else ok "Installé : ${cat}/${skill} [${agent}] → ${action}" (( count_install++ )) 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}" } ``` - [ ] **Étape 5 : Commit** ```bash rtk git add install.sh rtk git commit -m "feat: install.sh — menu fzf Gruvbox et installation des skills" ``` --- ## Task 9 : `install.sh` — écran de fin et `main()` **Files:** - Modify: `install.sh` - [ ] **Étape 1 : Ajouter `print_summary()`** ```bash # ── Récapitulatif final ─────────────────────────────────────────── print_summary() { local installed_skills=() for entry in "${SKILLS_LIST[@]}"; do IFS='|' read -r cat skill agent etat repo_ver local_ver <<< "$entry" local key="${cat}_${skill}_${agent}" local action; action=$(state_get "$key") [[ "$action" != "skip" ]] && installed_skills+=("${skill}|${agent}") done if [[ ${#installed_skills[@]} -gt 0 ]]; then echo -e "\n${GRV_PURPLE}╔══ Tester vos skills ══╗${RESET}\n" local shown_agents=() for item in "${installed_skills[@]}"; do IFS='|' read -r skill agent <<< "$item" local already_shown=0 for a in "${shown_agents[@]:-}"; do [[ "$a" == "${skill}|${agent}" ]] && already_shown=1; done [[ "$already_shown" -eq 1 ]] && continue shown_agents+=("${skill}|${agent}") case "$agent" in claude-code) echo -e " ${GRV_AQUA}claude \"utilise le skill ${skill}\" --print${RESET}" ;; gemini-cli) echo -e " ${GRV_AQUA}gemini -p \"utilise le skill ${skill}\"${RESET}" ;; codex) echo -e " ${GRV_AQUA}codex \"\$${skill}\"${RESET}" ;; hermes) echo -e " ${GRV_AQUA}hermes \"utilise le skill ${skill}\"${RESET}" ;; esac done fi echo -e "\n${GRV_PURPLE}╔══ Documentation agents ══╗${RESET}\n" 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" ;; codex) echo -e " ${GRV_BLUE}Codex ${RESET} → https://developers.openai.com/codex/skills" ;; hermes) echo -e " ${GRV_BLUE}Hermes ${RESET} → https://hermes-agent.nousresearch.com/docs/user-guide/features/skills/" ;; esac done echo "" } ``` - [ ] **Étape 2 : Ajouter `main()` et l'appel final** ```bash # ── Point d'entrée ──────────────────────────────────────────────── main() { echo -e "\n${GRV_PURPLE}╔══════════════════════════════════════╗${RESET}" echo -e "${GRV_PURPLE}║ mes_skills — Installeur de skills ║${RESET}" echo -e "${GRV_PURPLE}╚══════════════════════════════════════╝${RESET}\n" check_deps detect_agents clone_repo scan_skills if [[ ${#SKILLS_LIST[@]} -eq 0 ]]; then warn "Aucun skill compatible trouvé. Vérifier les agents détectés ou SKILLS_TAG." exit 0 fi run_menu install_selected print_summary } main "$@" ``` - [ ] **Étape 3 : Vérifier la syntaxe bash** ```bash bash -n install.sh ``` Résultat attendu : aucune sortie (syntaxe valide). - [ ] **Étape 4 : Commit** ```bash rtk git add install.sh rtk git commit -m "feat: install.sh — écran de fin, commandes test et point d'entrée main()" ``` --- ## Task 10 : Tests bash **Files:** - Create: `tests/test_install.sh` - [ ] **Étape 1 : Créer le runner de tests** Créer `tests/test_install.sh` : ```bash #!/usr/bin/env bash # Tests unitaires pour install.sh set -euo pipefail PASS=0 FAIL=0 assert_eq() { local desc="$1" expected="$2" actual="$3" if [[ "$expected" == "$actual" ]]; then echo " ✓ $desc" (( PASS++ )) else echo " ✗ $desc" echo " attendu : '$expected'" echo " obtenu : '$actual'" (( FAIL++ )) fi } assert_true() { local desc="$1" shift if eval "$@" &>/dev/null; then echo " ✓ $desc" (( PASS++ )) else echo " ✗ $desc" (( FAIL++ )) fi } # Charger les fonctions de install.sh sans exécuter main() # (on commente temporairement l'appel main pour les tests) source_install() { # Extraire toutes les fonctions sauf main() et son appel sed '/^main "\$@"/d' ../install.sh > /tmp/install_testable.sh sed -i 's/^main()/# main()/' /tmp/install_testable.sh || true source /tmp/install_testable.sh } echo "" echo "══ Tests install.sh ══" echo "" # ── Test 1 : Comparaison de versions ────────────────────────────── echo "1. Comparaison de versions" source_install version_is_newer "1.0.0" "1.2.0" assert_true "1.2.0 > 1.0.0" "version_is_newer '1.0.0' '1.2.0'" ! version_is_newer "1.2.0" "1.0.0" 2>/dev/null assert_true "1.0.0 n'est pas > 1.2.0" "! version_is_newer '1.2.0' '1.0.0'" ! version_is_newer "1.0.0" "1.0.0" 2>/dev/null assert_true "1.0.0 == 1.0.0 → pas plus récent" "! version_is_newer '1.0.0' '1.0.0'" # ── Test 2 : Parsing frontmatter ────────────────────────────────── echo "" echo "2. Parsing frontmatter" cat > /tmp/test_skill_$$.md << 'EOF' --- name: test-skill version: 2.1.0 description: Skill de test agents: [claude-code] category: dev tags: [bash, test] --- # Contenu EOF result=$(get_frontmatter_field "/tmp/test_skill_$$.md" "version") assert_eq "version parsée correctement" "2.1.0" "$result" result=$(get_frontmatter_field "/tmp/test_skill_$$.md" "name") assert_eq "name parsé correctement" "test-skill" "$result" rm -f "/tmp/test_skill_$$.md" # ── Test 3 : get_dest_path ──────────────────────────────────────── echo "" echo "3. Chemins de destination" result=$(get_dest_path "dev" "debugging" "claude-code" "global") assert_eq "chemin global claude-code" "$HOME/.claude/skills/dev/debugging/SKILL.md" "$result" result=$(get_dest_path "dev" "debugging" "gemini-cli" "local") assert_eq "chemin local gemini-cli" ".gemini/skills/dev/debugging/SKILL.md" "$result" result=$(get_dest_path "jardinage" "arrosage" "codex" "global") assert_eq "chemin global codex jardinage" "$HOME/.codex/skills/jardinage/arrosage/SKILL.md" "$result" # ── Test 4 : État fichier (state_get/state_cycle) ───────────────── echo "" echo "4. Gestion d'état" STATE_FILE=$(mktemp) echo "dev_debugging_claude_code=local" > "$STATE_FILE" result=$(state_get "dev_debugging_claude_code") assert_eq "state_get retourne 'local'" "local" "$result" state_cycle "dev_debugging_claude_code" "ok" result=$(state_get "dev_debugging_claude_code") assert_eq "cycle local→global" "global" "$result" state_cycle "dev_debugging_claude_code" "ok" result=$(state_get "dev_debugging_claude_code") assert_eq "cycle global→skip" "skip" "$result" state_cycle "dev_debugging_claude_code" "upd" result=$(state_get "dev_debugging_claude_code") assert_eq "cycle skip→update (si upd)" "update" "$result" state_cycle "dev_debugging_claude_code" "upd" result=$(state_get "dev_debugging_claude_code") assert_eq "cycle update→local" "local" "$result" rm -f "$STATE_FILE" # ── Bilan ───────────────────────────────────────────────────────── echo "" echo "══ Résultats : ${PASS} passés, ${FAIL} échoués ══" echo "" [[ "$FAIL" -eq 0 ]] ``` - [ ] **Étape 2 : Exécuter les tests** ```bash cd tests && bash test_install.sh ``` Résultat attendu : ``` ══ Tests install.sh ══ 1. Comparaison de versions ✓ 1.2.0 > 1.0.0 ✓ 1.0.0 n'est pas > 1.2.0 ✓ 1.0.0 == 1.0.0 → pas plus récent 2. Parsing frontmatter ✓ version parsée correctement ✓ name parsé correctement 3. Chemins de destination ✓ chemin global claude-code ✓ chemin local gemini-cli ✓ chemin global codex jardinage 4. Gestion d'état ✓ state_get retourne 'local' ✓ cycle local→global ✓ cycle global→skip ✓ cycle skip→update (si upd) ✓ cycle update→local ══ Résultats : 13 passés, 0 échoués ══ ``` - [ ] **Étape 3 : Test de dry-run de install.sh** ```bash SKILLS_DRY_RUN=1 SKILLS_REPO=. bash install.sh ``` Résultat attendu : le menu fzf s'ouvre, et après sélection les lignes `[DRY-RUN] cp ...` s'affichent sans écrire de fichiers. - [ ] **Étape 4 : Test de débogage** ```bash SKILLS_DEBUG=1 SKILLS_DRY_RUN=1 SKILLS_REPO=. bash install.sh ``` Résultat attendu : lignes `[DBG]` visibles en gris entre chaque étape. - [ ] **Étape 5 : Commit final** ```bash cd .. chmod +x install.sh tests/test_install.sh rtk git add install.sh tests/test_install.sh rtk git commit -m "feat: tests bash et install.sh complet — v1 prête" ``` --- ## Auto-revue de couverture spec | Exigence spec | Tâche | |---------------|-------| | Structure dépôt par catégorie | Task 1 | | Templates par agent | Task 1 | | Dossier `web/` réservé | Task 1 | | Frontmatter avec `tags` | Tasks 1-2 | | `docs/structure_skill.md` avec liens | Task 2 | | `docs/structure_repo.md` | Task 3 | | `docs/structure_script_install.md` | Task 3 | | `README.md` | Task 4 | | `evolution.md` | Task 4 | | Palette Gruvbox Dark 256 | Task 5 | | Thème fzf Gruvbox | Task 8 | | Zéro sudo | Task 6, 8 | | Détection agents (2 méthodes) | Task 6 | | Installation fzf si absent | Task 6 | | Clone /tmp | Task 7 | | Comparaison versions semver | Task 7 | | Menu fzf + TAB cycle | Task 8 | | Légende permanente | Task 8 | | `install_selected()` | Task 8 | | Écran de fin + commandes test | Task 9 | | Liens docs agents détectés | Task 9 | | `SKILLS_DEBUG/DRY_RUN/REPO/TAG/AGENT` | Tasks 5, 10 | | Tests unitaires bash | Task 10 | | Tests d'intégration dry-run | Task 10 |