---
name: blog-post
description: "Générer des articles de blog SEO-optimisés pour un site — recherche de niche, propositions d'idées, rédaction, intégration dans le site. Utiliser quand l'utilisateur veut créer du contenu blog qui capture du trafic organique longue traîne pour son produit ou son site."
allowed-tools:
  - Bash
  - Read
  - Write
  - Edit
  - Grep
  - Glob
  - WebSearch
  - WebFetch
  - AskUserQuestion
---

# Blog Post Generator

Tu es le Blog Post Generator.
Ta mission : analyser le produit ou le site de l'utilisateur, comprendre sa niche en profondeur, et produire des articles de blog SEO-optimisés qui capturent du trafic longue traîne.

Parse `$ARGUMENTS`:
- Le premier argument est le **nom ou l'URL du site/produit** (ex: `mon-saas`, `https://exemple.com`)
- `--ideas-only` → Proposer 5 idées d'articles sans en générer
- `--setup` → Installer l'infra blog sur le projet sans générer d'article
- `--list` → Lister les articles existants du projet
- `--no-deploy` → Générer sans déployer
- Par défaut, proposer le déploiement après génération (build + deploy + soumission aux moteurs de recherche)
- (pas d'argument) → Demander à l'utilisateur quel site/produit avec AskUserQuestion

---

## STEP 0 : Identifier le produit et sa niche

### 0a. Comprendre le produit

Rassembler le contexte disponible, dans cet ordre :
1. **Le dépôt courant** : lire `README.md`, la page d'accueil / landing (composants, contenu), tout document produit présent (docs de positionnement, personas, analyse concurrentielle, etc.)
2. **Le site en production** (si une URL existe) : `WebFetch(url, "Décris le produit, la cible, la proposition de valeur")`
3. **L'utilisateur** : si le contexte reste insuffisant (niche, cible, géographie, concurrents), poser les questions manquantes avec AskUserQuestion plutôt que de deviner

Extraire : nom du produit, proposition de valeur, marché cible, URL de production, concurrents connus, mots-clés déjà visés.

### 0b. Détecter l'infrastructure blog existante

Détecter où vivent les articles dans le projet courant. Chercher les patterns les plus courants :

```
Glob("content/blog/**/*.md")
Glob("content/blog/**/*.mdx")
Glob("src/content/**/*.{md,mdx}")        # Astro content collections
Glob("posts/**/*.{md,mdx}")
Glob("_posts/**/*.{md,markdown}")        # Jekyll
Glob("src/data/articles.*")              # Registre TS/JS d'articles
Glob("src/pages/blog/**/*")
Glob("app/blog/**/*")                    # Next.js App Router
Glob("src/components/blog/**/*")
```

Trois cas possibles :
- **Structure trouvée** → l'utiliser telle quelle, respecter le format existant (frontmatter, nommage, registre)
- **Structure ambiguë ou multiple** → demander à l'utilisateur laquelle utiliser
- **Aucune structure** → demander à l'utilisateur où doivent vivre ses articles, ou noter qu'il faudra exécuter le setup (STEP 5)

### 0c. Lister les articles existants (si `--list`)

Si flag `--list` :
- Lire les articles du dossier détecté et extraire les frontmatter
- Afficher un tableau : titre, date, slug, mot-clé cible
- FIN.

### 0d. Synthèse niche

Produire une fiche niche interne :
```
PRODUIT : [Nom]
NICHE : [Description ultra-précise du vertical]
SEGMENT : [Profil exact du client cible]
GÉO : [Marché géographique ciblé]
LANGUE : [langue du site, détectée via <html lang> ou demandée]
CONCURRENTS : [3-5 concurrents identifiés]
URL : [URL live si déployée]
MOTS-CLÉS PRINCIPAUX : [5-8 mots-clés core]
PROBLÈMES CLÉS : [3-5 pain points du persona]
```

**Si `--setup`** : exécuter uniquement STEP 0 + STEP 5 (installation de l'infra blog), puis FIN — ne pas générer d'article.

---

## STEP 1 : Recherche web — Opportunités de contenu SEO

### 1a. Analyse des mots-clés longue traîne

Rechercher pour la niche du produit :
```
WebSearch("[niche] guide 2026")
WebSearch("[niche] problème [pain point principal]")
WebSearch("[mot-clé principal] comment faire")
WebSearch("[niche] logiciel comparatif")
WebSearch("[niche] réglementation [pays/région cible] 2026")
```

Identifier :
- Questions fréquentes (People Also Ask)
- Sujets peu couverts par les concurrents
- Mots-clés avec intention informationnelle

### 1b. Analyse du contenu concurrent

Pour chaque concurrent identifié :
```
WebSearch("site:[concurrent-domain] blog")
WebFetch("[concurrent-blog-url]", "List all blog article titles and topics")
```

Identifier :
- Sujets que les concurrents couvrent (pour faire mieux)
- Sujets qu'ils NE couvrent PAS (opportunités)
- Formats qui marchent (guides, comparatifs, how-to, études de cas)

### 1c. Tendances et actualités

```
WebSearch("[niche] actualités 2026")
WebSearch("[niche] tendances nouvelles réglementations")
```

---

## STEP 2 : Proposer 5 idées d'articles

Générer exactement 5 propositions d'articles. Chaque proposition contient :

```markdown
### Idée [N] : [Titre de l'article]

- **Mot-clé cible** : [keyword principal visé]
- **Mots-clés secondaires** : [3-5 keywords LSI]
- **Intent** : [Informationnel / Transactionnel / Comparatif]
- **Volume estimé** : [Faible / Moyen / Élevé] (basé sur la recherche)
- **Difficulté** : [Facile / Moyen / Difficile]
- **Angle unique** : [Ce qui différencie cet article de la concurrence]
- **Format** : [Guide complet / How-to / Comparatif / Checklist / Étude de cas]
- **Longueur cible** : [1200-2000 mots]
- **CTA naturel** : [Comment le produit s'insère naturellement]
```

### Critères de sélection des idées

Prioriser les articles qui :
1. **Ciblent un problème réel** du persona (pas du contenu générique)
2. **Ont un volume de recherche** suffisant (basé sur les PAA et suggestions)
3. **Sont faisables** pour un nouveau domaine (pas de keywords ultra-compétitifs)
4. **Mènent naturellement** vers le produit (le CTA ne semble pas forcé)
5. **Sont evergreen** — pertinents pendant 12+ mois

### Types d'articles à mixer

- **Guide complet** : "Le guide complet du [processus clé] en 2026"
- **Comparatif** : "[Solution A] vs [Solution B] : quel outil choisir ?"
- **How-to** : "Comment [résoudre le pain point] en [X] étapes"
- **Checklist** : "Checklist [niche] : [X] points à vérifier avant [événement]"
- **Problème/solution** : "Pourquoi [problème] et comment y remédier"

### Présentation et validation

Afficher les 5 idées à l'utilisateur avec AskUserQuestion :
- Options = les 5 titres d'articles
- L'utilisateur choisit lequel générer

Si `--ideas-only` → afficher les idées et FIN.

---

## STEP 3 : Générer l'article

### 3a. Structure de l'article

```markdown
---
title: "[Titre optimisé SEO — 50-60 caractères]"
description: "[Meta description — 150-160 caractères]"
slug: "[slug-kebab-case]"
date: "[YYYY-MM-DD]"
author: "[Nom de l'entreprise ou de l'auteur]"
keywords:
  - "[mot-clé principal]"
  - "[mot-clé secondaire 1]"
  - "[mot-clé secondaire 2]"
  - "[mot-clé secondaire 3]"
category: "[catégorie thématique]"
readingTime: "[X] min"
---

# [Titre H1 — peut différer légèrement du title SEO]

[Introduction engageante — 100-150 mots. Poser le problème, promettre la solution. Inclure le mot-clé principal dans les 100 premiers mots.]

## [H2 — Section 1 : Contexte / Pourquoi c'est important]

[300-400 mots. Données, statistiques, contexte du marché.]

## [H2 — Section 2 : Le cœur du sujet]

[400-500 mots. Le contenu principal, structuré en sous-sections H3 si nécessaire.]

### [H3 — Sous-section si pertinent]

[Détails, exemples concrets, conseils pratiques.]

## [H2 — Section 3 : Guide pratique / Comment faire]

[300-400 mots. Étapes concrètes, actionnable.]

## [H2 — Section 4 : Erreurs courantes / Points d'attention]

[200-300 mots. Ce qu'il faut éviter.]

## [H2 — Conclusion]

[100-150 mots. Résumé, CTA naturel vers le produit. NE PAS être agressif commercialement — subtil et utile.]

---

*Cet article a été rédigé par l'équipe [Nom du produit]. [Une phrase sur le produit avec lien vers la page d'accueil.]*
```

Adapter le frontmatter au format déjà utilisé par les articles existants du projet, s'il y en a.

### 3b. Règles de rédaction SEO

1. **Mot-clé principal** dans : title, H1, meta description, premier paragraphe, un H2, conclusion
2. **Mots-clés secondaires** : distribués naturellement dans le texte (2-3 occurrences chacun)
3. **Liens internes** : vers d'autres articles du blog (si existants) et vers la page d'accueil / landing
4. **Structure** : H2 toutes les 300-400 mots, H3 pour les sous-sections
5. **Paragraphes courts** : 3-4 phrases max par paragraphe
6. **Listes à puces** : au moins 2 dans l'article pour la lisibilité
7. **Données concrètes** : chiffres, statistiques, exemples réels quand possible
8. **CTA** : 1 seul CTA principal, en conclusion, naturel et non-agressif
9. **Ton** : professionnel mais accessible, expert mais pas condescendant
10. **Longueur** : 1200-2000 mots (viser 1500 pour la plupart des articles)

### 3c. Règles de langue française

**Si le site est dans une autre langue que le français, transposer ces règles à sa langue.**

- **Accents obligatoires** : é, è, ê, à, ù, ç, ô, î partout
- Ex: "Créez" pas "Creez", "gérer" pas "gerer", "réglementation" pas "reglementation"
- Guillemets français « » pour les citations
- Espace insécable avant : ; ! ? (convention typographique FR)
- Écriture inclusive NON utilisée (rester classique)

---

## STEP 4 : Écrire le fichier article

Écrire le fichier markdown à l'emplacement détecté au STEP 0b (ou indiqué par l'utilisateur) :

```
{BLOG_CONTENT_PATH}/{slug}.md
```

Respecter les conventions existantes du projet : extension (`.md` / `.mdx`), format de frontmatter, convention de nommage (certains générateurs statiques exigent un préfixe date, ex. `YYYY-MM-DD-slug.md` pour Jekyll).

---

## STEP 5 : Setup infra blog (première fois uniquement)

Si le projet n'a PAS encore d'infrastructure blog, installer ce qui manque **en s'adaptant à la stack détectée** :

- **Next.js / Astro / Gatsby / Nuxt / Hugo / Jekyll / autre générateur** : utiliser les mécanismes natifs du framework (content collections, MDX, routes de contenu). Ne pas réinventer un système custom si le framework en fournit un.
- **Site HTML statique** : générer des pages HTML d'articles + une page index de blog, dans le style du site existant.
- **SPA React + Vite** (pas de système de contenu natif) : le pattern de référence ci-dessous fonctionne bien.

Dans tous les cas, lire les composants/pages existants du site pour respecter son style visuel, puis valider l'approche avec l'utilisateur avant de créer les fichiers.

### Pattern de référence pour SPA React + Vite

#### 5a. Installer les dépendances

```bash
npm install react-markdown react-helmet-async remark-gfm
```

#### 5b. Créer les composants blog

##### `src/components/blog/BlogSEO.tsx`
```typescript
import { Helmet } from "react-helmet-async";

interface BlogSEOProps {
  title: string;
  description: string;
  slug: string;
  date: string;
  author: string;
  keywords: string[];
  siteUrl: string;
}

export default function BlogSEO({ title, description, slug, date, author, keywords, siteUrl }: BlogSEOProps) {
  const url = `${siteUrl}/blog/${slug}`;
  return (
    <Helmet>
      <title>{title}</title>
      <meta name="description" content={description} />
      <meta name="keywords" content={keywords.join(", ")} />
      <meta name="author" content={author} />
      <link rel="canonical" href={url} />
      <meta property="og:type" content="article" />
      <meta property="og:title" content={title} />
      <meta property="og:description" content={description} />
      <meta property="og:url" content={url} />
      <meta property="article:published_time" content={date} />
      <meta property="article:author" content={author} />
      <meta name="twitter:card" content="summary_large_image" />
      <meta name="twitter:title" content={title} />
      <meta name="twitter:description" content={description} />
      <script type="application/ld+json">
        {JSON.stringify({
          "@context": "https://schema.org",
          "@type": "BlogPosting",
          headline: title,
          description,
          datePublished: date,
          author: { "@type": "Organization", name: author },
          publisher: { "@type": "Organization", name: author },
          url,
        })}
      </script>
    </Helmet>
  );
}
```

##### `src/components/blog/BlogArticle.tsx`
```typescript
import ReactMarkdown from "react-markdown";
import remarkGfm from "remark-gfm";

interface BlogArticleProps {
  content: string;
}

export default function BlogArticle({ content }: BlogArticleProps) {
  return (
    <article className="prose prose-lg max-w-3xl mx-auto px-4 py-12">
      <ReactMarkdown remarkPlugins={[remarkGfm]}>{content}</ReactMarkdown>
    </article>
  );
}
```

##### `src/components/blog/BlogList.tsx`
```typescript
import { Link } from "react-router-dom";
import type { ArticleMeta } from "@/data/articles";

interface BlogListProps {
  articles: ArticleMeta[];
}

export default function BlogList({ articles }: BlogListProps) {
  const sorted = [...articles].sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime());
  return (
    <div className="max-w-4xl mx-auto px-4 py-12">
      <h1 className="text-3xl font-bold mb-8">Blog</h1>
      <div className="grid gap-6">
        {sorted.map((article) => (
          <Link key={article.slug} to={`/blog/${article.slug}`} className="block border rounded-lg p-6 hover:shadow-lg transition-shadow">
            <div className="flex items-center gap-2 mb-2">
              <span className="text-xs font-medium bg-gray-100 rounded px-2 py-1">{article.category}</span>
              <span className="text-sm text-gray-500">{article.readingTime}</span>
            </div>
            <h2 className="text-xl font-semibold">{article.title}</h2>
            <p className="text-gray-600 mt-1">{article.description}</p>
            <span className="text-sm text-gray-500 mt-2 block">
              {/* adapter la locale à la langue du site */}
              {new Date(article.date).toLocaleDateString("fr-FR")}
            </span>
          </Link>
        ))}
      </div>
    </div>
  );
}
```

Si le projet utilise une librairie de composants (shadcn/ui, MUI, etc.), utiliser ses composants Card/Badge plutôt que du HTML brut, pour rester cohérent avec le reste du site.

##### `src/data/articles.ts`
```typescript
export interface ArticleMeta {
  slug: string;
  title: string;
  description: string;
  date: string;
  author: string;
  keywords: string[];
  category: string;
  readingTime: string;
}

// Import articles content as raw strings
// Each new article adds an import + entry here

export const articles: Record<string, { meta: ArticleMeta; content: string }> = {};
```

Note : chaque nouvel article ajoute un `import` raw du `.md` et une entrée dans `articles`.
Utiliser le pattern Vite raw import : `import content from "../../content/blog/slug.md?raw";`

##### `src/pages/BlogListPage.tsx`
```typescript
import { articles } from "@/data/articles";
import BlogList from "@/components/blog/BlogList";
import BlogSEO from "@/components/blog/BlogSEO";

export default function BlogListPage() {
  const metas = Object.values(articles).map((a) => a.meta);
  return (
    <>
      <BlogSEO
        title="Blog — [Nom du produit]"
        description="Articles et guides sur [niche du produit]"
        slug="blog"
        date={new Date().toISOString().split("T")[0]}
        author="[Nom de l'entreprise]"
        keywords={["blog", "[niche]"]}
        siteUrl="[URL_PRODUCTION]"
      />
      <BlogList articles={metas} />
    </>
  );
}
```

##### `src/pages/BlogArticlePage.tsx`
```typescript
import { useParams, Navigate } from "react-router-dom";
import { articles } from "@/data/articles";
import BlogArticle from "@/components/blog/BlogArticle";
import BlogSEO from "@/components/blog/BlogSEO";

export default function BlogArticlePage() {
  const { slug } = useParams<{ slug: string }>();
  const article = slug ? articles[slug] : undefined;

  if (!article) return <Navigate to="/blog" replace />;

  return (
    <>
      <BlogSEO {...article.meta} siteUrl="[URL_PRODUCTION]" />
      <BlogArticle content={article.content} />
    </>
  );
}
```

#### 5c. Ajouter les routes blog

Dans le routeur du projet (ex. `App.tsx`) :
```typescript
import BlogListPage from "@/pages/BlogListPage";
import BlogArticlePage from "@/pages/BlogArticlePage";

// Dans les Routes :
<Route path="/blog" element={<BlogListPage />} />
<Route path="/blog/:slug" element={<BlogArticlePage />} />
```

#### 5d. Wrapper HelmetProvider

Ajouter `HelmetProvider` dans `main.tsx` ou `App.tsx` :
```typescript
import { HelmetProvider } from "react-helmet-async";
// Wrapper : <HelmetProvider><App /></HelmetProvider>
```

#### 5e. Ajouter lien Blog dans la navbar

Trouver le composant navbar/header du projet et ajouter un lien vers `/blog`.

#### 5f. Configurer Vite pour l'import raw de .md

Vite supporte `?raw` nativement, mais ajouter la déclaration TypeScript dans `src/vite-env.d.ts` (ou l'équivalent) :
```typescript
declare module "*.md?raw" {
  const content: string;
  export default content;
}
```

**Attention SEO pour les SPA** : une SPA pure ne sert pas de HTML pré-rendu aux crawlers. Pour une indexation fiable, recommander un prerender (prerendering au build, SSG, ou migration vers un framework SSR/SSG) — le signaler à l'utilisateur si son blog est en SPA.

---

## STEP 6 : Intégrer le nouvel article

### 6a. Enregistrer l'article dans le système du projet

Selon la structure détectée :
- **Générateur statique / content collections** : le fichier markdown suffit généralement — vérifier que la collection le prend en compte (schéma de frontmatter, dossier correct)
- **Registre manuel** (ex. `src/data/articles.ts`) : ajouter l'import et l'entrée du nouvel article :

```typescript
import {slug}Content from "../../content/blog/{slug}.md?raw";

// Dans articles:
"{slug}": {
  meta: {
    slug: "{slug}",
    title: "{title}",
    description: "{description}",
    date: "{date}",
    author: "{author}",
    keywords: [{keywords}],
    category: "{category}",
    readingTime: "{readingTime}",
  },
  content: {slug}Content,
},
```

### 6b. Mettre à jour le sitemap

Si le sitemap est généré automatiquement (plugin du framework), vérifier qu'il inclura la nouvelle URL. Sinon, lire `public/sitemap.xml` (ou l'équivalent) et ajouter une entrée :
```xml
<url>
  <loc>https://{URL_PRODUCTION}/blog/{slug}</loc>
  <lastmod>{YYYY-MM-DD}</lastmod>
  <changefreq>monthly</changefreq>
  <priority>0.7</priority>
</url>
```

Ajouter aussi `/blog` (liste) si pas encore présent :
```xml
<url>
  <loc>https://{URL_PRODUCTION}/blog</loc>
  <lastmod>{YYYY-MM-DD}</lastmod>
  <changefreq>weekly</changefreq>
  <priority>0.8</priority>
</url>
```

---

## STEP 7 : Build & deploy (sauf `--no-deploy`)

Demander confirmation à l'utilisateur avant de déployer (règle : jamais de deploy à l'aveugle).

### 7a. Build

Lancer la commande de build du projet (ex. `npm run build`, ou celle définie dans le `package.json` / la doc du projet).

Vérifier que le build réussit. Si erreurs TypeScript → corriger et relancer le build.

### 7b. Deploy

Utiliser la méthode de déploiement habituelle du projet :
- Si un script ou une commande de deploy existe (script `deploy` dans `package.json`, `vercel --prod`, `netlify deploy --prod`, CI déclenchée par un push git, etc.), l'utiliser
- Si la méthode de deploy est inconnue → demander à l'utilisateur comment il déploie son site (ne jamais deviner un deploy en production)

Capturer l'URL de déploiement.

### 7c. Soumission aux moteurs de recherche (après deploy réussi)

Aider l'indexation des nouvelles URLs :
- **Google** : soumettre l'URL dans Google Search Console (Inspection d'URL → Demander une indexation), et vérifier que le sitemap y est déclaré. Afficher le lien : `https://search.google.com/search-console`
- **Bing / autres** : si le site a une clé IndexNow, soumettre les nouvelles URLs via l'API IndexNow (`https://api.indexnow.org/indexnow`). Sinon, proposer d'en configurer une (fichier clé à la racine du site)

---

## STEP 8 : Résumé

Afficher à l'utilisateur :

```
## Blog Post Generator — [Nom du produit]

**Article généré** : [Titre]
**Slug** : /blog/[slug]
**Mot-clé cible** : [keyword]
**Longueur** : [X] mots

### Fichiers créés/modifiés
- `[chemin]/{slug}.md` — Article
- [Si registre] `src/data/articles.ts` — Registre mis à jour
- `public/sitemap.xml` — URL ajoutée
[Si setup] - Composants et pages blog installés

### SEO Checklist
- [x] Mot-clé dans title, H1, meta description, intro
- [x] Structure H2/H3 optimisée
- [x] Meta description 150-160 caractères
- [x] JSON-LD BlogPosting
- [x] Sitemap mis à jour
- [x] Lien interne vers la page d'accueil
- [x] Déployé
- [x] Soumission moteurs de recherche (GSC / IndexNow)

### Prochaines étapes recommandées
1. Relire l'article et ajuster le ton si nécessaire
2. Vérifier l'indexation dans Google Search Console sous 48 h
3. Partager l'article sur les communautés pertinentes de la niche (forums, groupes, newsletters)
```

---

## RÈGLES IMPORTANTES

1. **Accents et typographie de la langue du site OBLIGATOIRES (accents français si site FR)** — Vérifier chaque accent dans le contenu généré
2. **Pas de contenu générique** — Chaque article doit être 100% spécifique à la niche
3. **Pas de sur-optimisation** — L'article doit être utile d'abord, SEO ensuite
4. **CTA subtil** — Jamais "Inscrivez-vous maintenant !!!". Toujours naturel et contextuel
5. **Données vérifiables** — Ne pas inventer de statistiques. Utiliser des sources réelles ou formuler en relatif
6. **Respecter l'existant** — Format de frontmatter, conventions de nommage, style visuel : toujours s'aligner sur ce que le projet fait déjà
7. **Maillage interne** — Relier chaque nouvel article aux articles existants pertinents (et mettre à jour les anciens articles avec un lien vers le nouveau quand c'est pertinent)
8. **Vite raw import** : si le projet utilise le pattern registre + Vite, toujours utiliser le suffixe `?raw` pour les imports .md
9. **Prose styling** : pour le rendu des articles en React/Tailwind, utiliser `prose` (Tailwind Typography). Si pas installé, ajouter `@tailwindcss/typography`
10. **Jamais de deploy à l'aveugle** — si la méthode de déploiement n'est pas claire, demander avant d'agir
