Files
mes_skills/docs/superpowers/plans/2026-05-16-repo-et-installeur.md
T

1567 lines
46 KiB
Markdown

# 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/<categorie>/<nom>/claude-code.md`
**Destination globale :** `~/.claude/skills/<categorie>/<nom>/SKILL.md`
**Destination locale :** `.claude/skills/<categorie>/<nom>/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 <nom>" --print
```
---
## Gemini CLI
**Docs :** https://github.com/google-gemini/gemini-cli/blob/main/docs/cli/skills.md
**Fichier dans le dépôt :** `skills/<categorie>/<nom>/gemini-cli.md`
**Destination globale :** `~/.gemini/skills/<categorie>/<nom>/SKILL.md`
**Destination locale :** `.gemini/skills/<categorie>/<nom>/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 <nom>"
```
---
## Codex (OpenAI)
**Docs :** https://developers.openai.com/codex/skills
**Fichier dans le dépôt :** `skills/<categorie>/<nom>/codex.md`
**Destination globale :** `~/.codex/skills/<categorie>/<nom>/SKILL.md`
**Destination locale :** `.codex/skills/<categorie>/<nom>/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 "$<nom>"
```
---
## Hermes Agent (NousResearch)
**Docs :** https://hermes-agent.nousresearch.com/docs/user-guide/features/skills/
**Fichier dans le dépôt :** `skills/<categorie>/<nom>/hermes.md`
**Destination globale :** `~/.hermes/skills/<categorie>/<nom>/SKILL.md`
**Destination locale :** `.hermes/skills/<categorie>/<nom>/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 <nom>"
```
```
- [ ] **É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
│ ├── <categorie>/ ← catégorie (dev, infra, jardinage, ...)
│ │ └── <nom-skill>/ ← 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 : `<agent>.md` (ex: `claude-code.md`)
- Version : semver `X.Y.Z` dans le frontmatter
- Tags : minuscules, sans espace (ex: `[bash, debug]`)
- L'installeur copie `<agent>.md` → `SKILL.md` dans la destination
## Ajouter un nouveau skill
1. Créer le dossier : `skills/<categorie>/<nom>/`
2. Copier un template depuis `templates/<agent>.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_<PID>/
scan_skills()
└─ pour chaque skills/<cat>/<nom>/<agent>.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_<PID>]
└─ TAB → cycle_action() + reload
└─ ENTER → liste des sélections
install_selected()
└─ mkdir -p destination
└─ cp <agent>.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/<cat>/<nom>/SKILL.md` | `.claude/skills/<cat>/<nom>/SKILL.md` |
| gemini-cli | `~/.gemini/skills/<cat>/<nom>/SKILL.md` | `.gemini/skills/<cat>/<nom>/SKILL.md` |
| codex | `~/.codex/skills/<cat>/<nom>/SKILL.md` | `.codex/skills/<cat>/<nom>/SKILL.md` |
| hermes | `~/.hermes/skills/<cat>/<nom>/SKILL.md` | `.hermes/skills/<cat>/<nom>/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/<categorie>/<nom>/
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 |