---
name: wp-pack
description: "Zip/package WordPress (plugin ou thème). Se charge : (1) après modification de .php/.css/.js/.html/.twig/block.json/theme.json dans un projet WP (header 'Plugin Name:' ou 'Theme Name:' détecté), (2) dès qu'un zip/package WP est envisagé — mots-clés 'zip le plugin WP', 'package WordPress', 'zipper le thème', 'prépare pour prod WP', 'build du plugin WP'. Flow : détection type et slug → bump version (patch pour fix, minor pour feature) → zip via node hooks/wp-pack.js (jamais de Compress-Archive/zip -r inline, jamais de script .ps1/.sh temporaire). Ne se charge PAS : projet non-WordPress, review ou audit read-only, fichiers hors scope WP."
compatibility: "WordPress project required (plugin or theme with valid PHP header)"
---

# WP Pack — Packaging WordPress

Ce skill s'active :
- **Automatiquement** quand Claude termine une modification de code (fichiers PHP, CSS, JS, HTML, templates) et qu'un projet WordPress est détecté — **avec ou sans commande codebloom**
- **Sur demande** quand l'utilisateur demande de packager, zipper ou préparer pour distribution

**Le zip est TOUJOURS généré automatiquement après une modification ou un ajout de code. Ne JAMAIS demander "Tu veux le zip ?" — le faire directement.**

## Étape 1 — Détection auto : Plugin ou Thème

### 1a. Type et fichier principal

| Type | Détection |
|------|-----------|
| Plugin | Fichier PHP contenant `Plugin Name:` dans le header |
| Thème | `style.css` contenant `Theme Name:` dans le header |

Chercher d'abord à la racine. Si rien trouvé → chercher dans les sous-dossiers directs (1 niveau).

### 1b. Slug (CRITIQUE)

Le slug détermine le nom du dossier racine dans le zip. **Un mauvais slug = deux plugins sur le site WordPress.**

Déterminer le slug dans cet ordre de priorité :
1. **`Text Domain:`** dans le header du fichier principal (source la plus fiable)
2. **Nom du fichier PHP principal** sans extension (ex: `adn-tag.php` → `adn-tag`)
3. **Dernier recours** : nom du dossier contenant le fichier principal

**RÈGLE ABSOLUE** : le slug dans le zip doit être **identique** à celui déjà installé sur le site WordPress. Ne JAMAIS inventer, transformer ou dériver un slug autrement (pas de conversion `.` → `-`, pas de slugification du nom du plugin, etc.).

**RÈGLE STRUCTURE** : le dossier racine dans le zip doit **toujours** être `{slug}/`. Ne JAMAIS changer la structure interne du zip (pas de dossier racine renommé, pas de fichiers déplacés, pas de réorganisation). Un changement de structure = double plugin dans WordPress à l'activation.

### 1c. Structure

| Structure | Détection | Dossier source |
|-----------|-----------|----------------|
| **Racine** | Le fichier principal est à la racine du projet | `.` (racine) |
| **Sous-dossier** | Le fichier principal est dans un sous-dossier | Le sous-dossier |

Afficher avant de continuer :
```
Type : Plugin / Thème
Slug : {slug}
Source : {chemin du dossier source}
```

## Étape 2 — Bump de version

### Lire la version actuelle

Chercher la version dans le fichier principal détecté à l'étape 1.

### Déterminer l'incrément

| Type de changement | Bump | Exemple |
|--------|------|---------|
| Nouvelle feature / fonctionnalité | **minor** (0.X.0) | `1.2.3` → `1.3.0` |
| Correction / refactoring / nettoyage | **patch** (0.0.X) | `1.2.3` → `1.2.4` |
| Breaking change explicite | **major** (X.0.0) | `1.2.3` → `2.0.0` |
| Demande directe de l'utilisateur | **demander** | patch / minor / major / custom |

**Règles semver** :
- **major** (X.0.0) : remet minor et patch à 0
- **minor** (0.X.0) : remet patch à 0
- **patch** (0.0.X) : incrémente seulement

Quand le bump est auto-déterminé : afficher la version choisie et enchaîner directement.

### Fichiers à mettre à jour

Mettre à jour **seulement les fichiers qui existent** :

| Type | Fichiers | Champ |
|------|----------|-------|
| Plugin | PHP principal | `Version:` dans le header |
| Plugin | `readme.txt` | `Stable tag:` |
| Plugin | PHP principal ou includes | `define('..._VERSION', '...')` ou `const VERSION = '...'` |
| Thème | `style.css` | `Version:` dans le header |
| Thème | `functions.php` | `define('..._VERSION', '...')` ou `const VERSION = '...'` |
| Thème | `readme.txt` (si existe) | `Stable tag:` |

## Étape 3 — Anciens zips (géré automatiquement)

**Ne lancer aucune commande `rm` / `Remove-Item` / `del` manuelle.** Le script `wp-pack.js` supprime lui-même, via `fs.unlinkSync`, tous les anciens `{slug}.zip` et `{slug}-*.zip` présents dans le dossier source, le dossier parent, et le dossier de sortie, avant de créer le nouveau zip. Passer directement à l'étape 4.

## Étape 4 — Créer le zip

**Utiliser le script `wp-pack.js` du plugin.** Ne JAMAIS écrire de commande zip/PowerShell inline, et ne JAMAIS créer de script temporaire (`build-zip.sh`, `package.ps1`, etc.).

### ❌ Anti-patterns stricts — ne JAMAIS faire

```bash
# Interdit — rm + Compress-Archive inline (trigger un prompt de permission + bypass des exclusions du script)
cd "..." && rm -f {slug}.zip && powershell.exe -Command "Compress-Archive -Path '{slug}' -DestinationPath '{slug}.zip' -Force"

# Interdit — zip unix inline
cd "..." && rm -f {slug}.zip && zip -r {slug}.zip {slug}/

# Interdit — script temporaire
echo "..." > build-zip.ps1 && powershell.exe -File build-zip.ps1
```

Ces commandes :
- Déclenchent un prompt de permission sur `rm`
- Ne respectent **pas** les exclusions (`.git`, `node_modules`, `*.md`, `.claude/`, etc.)
- Ne nettoient **pas** les anciens zips du dossier parent / output
- Ne garantissent **pas** que le dossier racine du zip = `{slug}/`

### ✅ Commande unique et obligatoire

```bash
node "${CLAUDE_PLUGIN_ROOT}/hooks/wp-pack.js" --source "{chemin_dossier_source}" --slug "{slug}" --output "{chemin_output}/{slug}.zip"
```

**Arguments :**
- `--source` : chemin absolu du dossier contenant le code plugin/thème (racine ou sous-dossier)
- `--slug` : le slug détecté à l'étape 1b (détermine le nom du dossier racine dans le zip)
- `--output` : chemin absolu du zip de sortie

**Emplacement du zip :**
- Structure racine → le zip va dans le dossier source : `{source}/{slug}.zip`
- Structure sous-dossier → le zip va dans le dossier parent (racine projet) : `{parent}/{slug}.zip`

Le script gère automatiquement :
- **Windows** : PowerShell `Compress-Archive` via fichier .ps1 temporaire (pas de problème d'échappement)
- **macOS/Linux** : commande `zip`
- **Renommage du dossier** : si le dossier source a un nom différent du slug, le zip contient quand même `{slug}/` comme racine
- **Exclusions** : `.git`, `node_modules`, `*.md`, `.claude/`, `plans/`, `*.zip`, etc. (liste complète dans le script)
- `readme.txt` est toujours inclus (ce n'est pas un `.md`)

## Étape 5 — Vérification

### Contenu du zip

Lister le contenu résumé et vérifier :
- Présence du fichier principal (PHP header ou `style.css`)
- Absence de fichiers dev (`.git/`, `node_modules/`, `.env`)
- Dossier racine dans le zip = slug

### Résumé

```
Type     : Plugin / Thème
Slug     : {slug}
Version  : {version} (depuis {ancienne-version})
Zip      : {slug}.zip ({taille})
Fichiers : {nombre} fichiers
```

## Cas particuliers

### `vendor/` (Composer)

- Si `composer.json` existe avec une section `autoload` → `vendor/` est inclus automatiquement
- Si `vendor/` n'existe pas mais `composer.json` a un autoload → signaler : "vendor/ manquant — lancer `composer install --no-dev`"

### Build step détecté

- Si `package.json` contient des scripts `build`, `compile`, ou `dist` → signaler : "Build step détecté — vérifier que les assets sont à jour"

### Traductions (`languages/`)

- Toujours inclues si le dossier existe

### Pas de `readme.txt`

- Ne pas en créer
- Signaler uniquement si plugin destiné à WordPress.org

## Comportement

- **TOUJOURS zipper** après une modification ou un ajout de code dans un projet WordPress — ne JAMAIS demander "Tu veux le zip ?", le faire directement
- **Utiliser `wp-pack.js`** — ne JAMAIS écrire de commandes zip/PowerShell inline, ne JAMAIS créer de scripts (`build-zip.sh`, `package.ps1`, etc.)
- **Auto-bumper** la version selon le type de changement (minor pour feature, patch pour fix/refactoring)
- **Toujours bumper** — chaque zip inclut un bump de version (étapes 2→5), y compris depuis `/push`. Raison : forcer le cache-busting CSS/JS côté WordPress
- **Enchaîner** sans interruption : suppression → zip → vérification
- **Signaler** les anomalies (vendor manquant, build step, taille > 10 MB) sans bloquer
- **Ne pas modifier** la structure du projet
- **Ne pas créer** de `readme.txt`, `LICENSE`, ou autres fichiers manquants
