From tseo
Audit SEO technique source-only complet pour projets Laravel + Blade. Discovery → questions interactives → boucle thématique sur references/*.md (constat + acceptation user par thème ; si oui → apply auto-safe batch, auto-risky un par un, manuels en TODO+boilerplate ; si non → skip total) → recap final. Aucun crawl HTTP, aucune génération de contenu textuel. Refuse tout autre stack que Laravel. Use when user says: /seo-audit, audit SEO, optimisation SEO technique, lance un audit.
How this skill is triggered — by the user, by Claude, or both
Slash command
/tseo:seo-audit [project_path][project_path]The summary Claude sees in its skill listing — used to decide when to auto-load this skill
Tu es l'orchestrateur. Tu pilotes un pipeline en **4 phases** sur un projet Laravel : Discovery → Q&A → boucle thématique (Audit+Apply) → Recap final.
blade.mdboilerplates/smartlink.blade.phpboilerplates/smartlink.jsreferences/core-web-vitals.mdreferences/duplicate-content.mdreferences/head-meta.mdreferences/hreflang-i18n.mdreferences/http-headers.mdreferences/http-redirects.mdreferences/images.mdreferences/internal-linking.mdreferences/js-rendering.mdreferences/llm-geo.mdreferences/mobile-first.mdreferences/performance.mdreferences/robots-crawling.mdreferences/schema-jsonld.mdreferences/security-leaks.mdreferences/semantic-html.mdreferences/seo-checks.mdTu es l'orchestrateur. Tu pilotes un pipeline en 4 phases sur un projet Laravel : Discovery → Q&A → boucle thématique (Audit+Apply) → Recap final.
La Phase 3 boucle sur les 11 thèmes actifs de references/. Pour chaque thème : tu fais le constat (ce qui va / ce qui ne va pas), tu demandes à l'utilisateur s'il accepte les modifications. Si oui → tu appliques (auto-safe batch + auto-risky un par un + manuels en TODO). Si non → skip total (pas de TODO non plus). Puis tu passes au thème suivant.
Tu n'écris jamais :
feedback_no_content_drafting. Ces fields manquants → toujours tier: manual.resources/views/, public/{robots.txt,sitemap.xml,*.webmanifest,favicon.*}, app/Http/, routes/, config/. Jamais .env, vendor/, storage/, database/.file:line. Confidence < high jamais auto-fixé. Donnée manquante → data_unavailable, jamais inventer.git status sale → STOP, demande au user de stash/commit.skills/seo-audit/blade.md — sémantique runtime des directives Blade. À charger uniquement et exclusivement au moment où tu t'apprêtes à lire un fichier .blade.php, jamais avant, jamais "par précaution" en début de phase. En pratique : la première fois que tu ouvres un .blade.php dans la session courante (typiquement Phase 1.5 quand tu suis le mapping URL→view), charge blade.md une fois ; ensuite il reste en contexte pour les lectures Blade ultérieures (Phases 1.6, 1.7, 3.A). Si la session ne lit aucun .blade.php (cas refus stack non-Laravel, par exemple), ne pas le charger.
Le fichier explique comment interpréter @if/@else (XOR), @foreach (multiplication), @yield/@section/@stack/@push (composition), {!! !!} et @php (zones opaques), composants <x-…/>, etc. Sans ça → faux positifs garantis (double h1 dans un XOR, title "manquant" alors qu'il est @push'é, etc.).
Application concrète : avant de compter une occurrence (<h1>, <title>, <link rel="canonical">, <meta name="description">...) ou de signaler une absence, toujours :
@foreach/@for/@while) ;confidence: low ou data_unavailable sur les zones opaques ({!! !!}, @php, <x-dynamic-component>).$ARGUMENTS : chemin du projet cible. Si vide → utilise cwd mais demande confirmation explicite (affiche le chemin) avant de procéder.Détecte le framework, énumère les routes et le maillage, capture les signaux d'indexabilité. Produit <project_path>/.tseo/raw/site_model.json + les MD humains (framework.md, site-map.md, links.md, indexability.md, scale.md, pages/<slug>.md).
Lis les signatures :
| Signature | Décision |
|---|---|
composer.json contient "laravel/framework" ET artisan présent | ✅ adapter Laravel |
composer.json contient "laravel/framework" mais artisan absent | ⚠️ avertit, mode dégradé (regex sur routes/*.php au lieu de artisan route:list) |
Toute autre signature (next, wp-config.php, manage.py, Gemfile, *.html à plat sans Laravel) | ❌ refuse avec message roadmap |
Message de refus :
tseo v0.1 supporte uniquement les projets Laravel + Blade.
Stack détectée : <stack>
Évidence : <fichier signature>
Roadmap :
v0.4+ : HTML statique, Next.js (App Router), WordPress
v0.5+ : Symfony, Django, Rails
Lis en parallèle :
composer.json : version Laravel + dépendances SEO connues (spatie/laravel-sitemap, artesaos/seotools, spatie/schema-org, inertiajs/inertia-laravel, livewire/livewire)package.json : CSS framework (Tailwind, DaisyUI), build (Vite, Mix).env : APP_URL, APP_LOCALE, APP_FALLBACK_LOCALE, APP_ENVbootstrap/app.php : middleware globaux, health: routeroutes/web.php + routes/api.php (si existe)public/robots.txt (si existe)public/sitemap.xml (si existe)public/favicon.ico, public/favicon.svg, public/og-default.png (présence)Tente : cd <project_path> && php artisan route:list --json
routes/*.php. Marquer confidence: medium pour ces routes.Pour chaque route, attribue kind :
page : GET, action retourne view(...) (regex sur le contrôleur cible)json_endpoint : GET, action retourne JsonResponse ou response()->json(...)form_action : POST/PUT/PATCH/DELETEutility : closure inline non-vue (callback OAuth, redirect, debug)framework_internal : path provenant de vendor/laravel/...Pour chaque kind: page :
view\s*\(\s*['"]([^'"]+)['"]audit.create → resources/views/audit/create.blade.phpview: unknown avec confidence: low. Jamais inventer.Pour chaque vue, regex @extends\s*\(\s*['"]([^'"]+)['"] jusqu'à racine.
Pour le layout principal et chaque vue, capture la présence/absence de :
<html lang>, <meta charset>, <meta viewport>, <title> (direct ou via @yield)<meta name="description">, <meta name="robots">, <link rel="canonical"><link rel="alternate" hreflang="...">, <meta property="og:*">, <meta name="twitter:*"><script type="application/ld+json">, <link rel="icon">, favicon file<meta name="theme-color">Grep dans les vues :
route\s*\(\s*['"]([^'"]+)['"]href\s*=\s*['"]([^'"]+)['"]action\s*=\s*['"]([^'"]+)['"]fetch\s*\(, axiosConstruire internal_link_graph: {from, to, via, kind} et external_links: {to, rel, target, where}.
public/robots.txt (présent ? content ? directive Sitemap: ?)public/sitemap.xml (présent ?)app/Http/Middleware/ + bootstrap/app.php : grep X-Robots-Tag, noindex, header(Pour chaque route paramétrique :
cardinality: data_unavailablecardinality_resolution_options: ["read sitemap.xml", "query DB", "real HTTP crawl"]linked_from_internal_pages: <bool> (selon le graphe)orphan_from_internal_linking: <bool>Cherche dans routes/web.php : Route::group(['prefix' => '{locale}']) ou similaire. Si trouvé → marque i18n: true et collecte la liste des locales depuis config/app.php ou App::getLocales().
Écris :
<project>/.tseo/raw/site_model.json (shape complet, voir references/site-model-schema.md à venir v0.2)<project>/.tseo/{framework,site-map,links,indexability,scale}.md<project>/.tseo/pages/<slug>.md par page HTML<project>/.tseo/README.md qui explique le dossierSi .tseo/applied.md existe → c'est une re-run, charge la liste des findings déjà appliqués pour la Phase 4.
Pose des questions à l'utilisateur sur ce que la source ne peut pas dire. Utilise AskUserQuestion avec format choix multiples. Persiste les réponses dans <project>/.tseo/answers.json.
.tseo/answers.json existe déjàCharge les réponses précédentes. Pour chaque clé attendue, si déjà répondu → skip. Pose uniquement les nouvelles questions ou celles devenues pertinentes (nouvelles routes détectées par exemple).
Nom du site / organisation
answers.site_nameLocale principale
fr / en / es / de / Otheranswers.localeLocales additionnelles (i18n)
answers.locales (array, ex: ["fr"] ou ["fr","en","es"])Environnement
production / staging / development localanswers.environmentPour chaque entrée de site_model.page_routes_summary.dynamic_templates :
"La route <template> (ex: /audit/{audit}) doit-elle être indexée par Google ?"
answers.dynamic_routes_visibility[<template>]Si "Publique et indexable" :
<template> ? Influence la stratégie sitemap."answers.dynamic_routes_cardinality_estimate[<template>]Logo URL
answers.organization_logo_url (peut être null)Profils sociaux
sameAs dans Schema Organization. Sépare par des virgules."answers.organization_same_as (array, peut être vide)"Le site a-t-il une fonctionnalité de recherche interne (URL /search?q=...) ?"
answers.has_internal_searchSi oui : "Quelle est l'URL template de la recherche ? (ex: /search?q={search_term_string})"
answers.search_url_templateapp/Models/ :<Model> doit-il être inclus dans le sitemap (= les instances ont des URLs publiques) ?"
answers.sitemap_models[<Model>]Après chaque réponse, écrire <project>/.tseo/answers.json. Format :
{
"$schema_version": "0.1",
"captured_at": "<YYYY-MM-DDTHH:MM:SS>",
"site_name": "...",
"locale": "fr",
"locales": ["fr"],
"environment": "production",
"app_url": "<from .env or override>",
"dynamic_routes_visibility": { "/audit/{audit}": "private" },
"dynamic_routes_cardinality_estimate": {},
"organization_logo_url": null,
"organization_same_as": [],
"has_internal_search": false,
"search_url_template": null,
"sitemap_models": {}
}
lang="fr" est dans le layout, locale par défaut = "fr", confirme tacitement).AskUserQuestion (limite UI).La Phase 3 itère sur les 11 fichiers thématiques actifs de references/, dans l'ordre de l'index seo-checks.md. Pour chaque thème : évaluation des règles → constat affiché → décision utilisateur (accepter / skip) → application si accepté.
git -C <project_path> rev-parse --git-dir → est-ce un repo git ? Sinon STOP, "Le projet doit être un repo git pour permettre le rollback granulaire."git -C <project_path> status --porcelain → working tree vide ? Sinon STOP, "Working tree git sale. Stash ou commit tes changements avant de lancer l'audit."git -C <project_path> config user.email et user.name → configurés ? Sinon demande à l'utilisateur de les configurer (ne configure pas automatiquement).<project>/.tseo/applied.md s'il existe (mode incrémental).<project>/.tseo/findings.json (sera enrichi au fil des thèmes) :
{
"$schema_version": "0.1",
"audit_at": "<YYYY-MM-DDTHH:MM:SS>",
"themes": [],
"data_unavailable": []
}
references/seo-checks.md)Boucle dans l'ordre de l'index. Skip silencieux des fichiers scaffold (sans bloc ### XX-NNN) : http-redirects.md, http-headers.md, status-codes.md, url-structure.md, duplicate-content.md, core-web-vitals.md, mobile-first.md, llm-geo.md.
11 thèmes actifs traités séquentiellement :
head-meta.mdrobots-crawling.mdsitemap.mdinternal-linking.mdsemantic-html.mdimages.mdschema-jsonld.mdperformance.mdjs-rendering.mdhreflang-i18n.mdsecurity-leaks.mdParse les blocs ### <ID> · <Titre> du fichier. Pour chaque règle :
site_model.json + answers.json.{
"id": "HM-001",
"axis": "head-metadata",
"tier": "auto-safe",
"impact": "Low",
"confidence": "high",
"title": "Charset déclaré",
"evidence": [{"file": "...", "line": 3, "snippet": "..."}],
"fix": {"type": "insert_after", "anchor": {...}, "content": "..."},
"sandbox": ["resources/views/layouts/*.blade.php"],
"source_file": "head-meta.md",
"previously_applied": false
}
confidence < high ET tier déclaré auto-safe → re-classe en auto-risky (jamais d'auto-fix sur basse confidence).data_unavailable plutôt qu'un faux positif.Mode incrémental : si l'id + evidence matchent un finding déjà listé dans applied.md → previously_applied: true. Listé séparément, non re-proposé pour fix.
Affiche dans le terminal :
Thème <N>/11 — <theme name> (<theme>.md)
Ce qui va :
✅ HM-002 — Viewport déclaré (resources/views/layouts/app.blade.php:5)
✅ HM-003 — lang="fr" présent (resources/views/layouts/app.blade.php:1)
Ce qui ne va pas :
❌ HM-001 — Charset manquant [auto-safe, Low]
evidence: resources/views/layouts/app.blade.php:3
fix: insertion <meta charset="UTF-8">
❌ HM-005 — Meta description manquante [manual, High]
evidence: resources/views/layouts/app.blade.php (head)
fix: TODO manuel — boilerplate à insérer + section à compléter par page
❌ HM-006 — Canonical self-ref absent [auto-safe, Critical]
evidence: resources/views/layouts/app.blade.php (head)
fix: insertion <link rel="canonical" href="{{ url()->current() }}">
Total : 3 fixes (2 auto-safe, 1 manuel)
Cas particuliers :
previously_applied → affiche "Thème : tout déjà appliqué lors d'audits précédents.", pas de question, pause Étape 3.G.Demande via AskUserQuestion :
"Thème — accepter les modifications proposées ?"
- Oui : applique tous les
auto-safe+auto-riskydu thème, et écrit lesmanualen TODO.- Non : skip total — aucun fix appliqué, aucun TODO écrit pour ce thème.
Délègue à seo-apply en mode scopé sur les findings du thème courant uniquement. Suivre les sous-phases A/B/C de skills/seo-apply/SKILL.md :
Sous-phase A — Auto-safe batch (1 commit groupé pour le thème)
auto-safe du thème.fix.type).tseo: <theme>.md — apply <X> auto-safe fixes.applied.md avec IDs + commit SHA.Sous-phase B — Auto-risky un par un (1 commit par finding)
auto-risky du thème :
tseo: <id> — <title>.skipped_at_apply: <ts>, finding non re-proposé sans nouvelle exécution.Sous-phase C — Manuel → TODO
<project>/.tseo/manual-todos.md les TODO du thème (regroupés par sévérité).fix.boilerplate (ex: JL-001 Schema Organization) : insère le boilerplate dans le sandbox + commit séparé tseo: insert <id> boilerplate (manual completion required).applied.md (statut "boilerplate-only, awaits manual completion").Toutes les contraintes de seo-apply (sandbox strict, mtime check, repo clean au démarrage du thème) s'appliquent.
manual-todos.md.findings.json avec theme_decision: "skipped" pour traçabilité dans le recap.Après chaque thème, append dans <project>/.tseo/findings.json une entrée :
{
"theme": "head-meta.md",
"theme_decision": "accepted" | "skipped" | "all_ok" | "all_previously_applied",
"findings": [ ... ],
"applied_commits": ["abc1234", "def5678"],
"manual_todos_added": ["HM-005", "HM-007"]
}
Après chaque thème — qu'il ait abouti à un apply, un skip total, un all_ok, ou un all_previously_applied — ne jamais charger automatiquement le fichier de référence du thème suivant. Affiche un récap court (commits créés, IDs conformes, todos ajoutés) puis attends une instruction utilisateur explicite ("continue", "thème suivant", "passe au prochain", équivalents) avant de procéder au thème N+1.
Cette pause systématique permet à l'utilisateur de :
.tseo/.L'audit ne reprend qu'à l'instruction explicite ; aucun timer, aucun heuristique d'auto-advance.
Une fois la boucle des 11 thèmes terminée, génère <project>/.tseo/recap.md :
# Audit SEO — <site_name> — <YYYY-MM-DD>
## Résumé exécutif
- **<N>** items évalués sur 11 thèmes actifs
- **<X>** auto-safe appliqués (<C1> commits)
- **<Y>** auto-risky appliqués (<C2> commits) — **<Z>** skippés à la review
- **<W>** TODO manuels écrits dans `.tseo/manual-todos.md`
- **<S>** thèmes complètement skippés par l'utilisateur
- **<V>** items déjà appliqués lors d'audits précédents
## Bilan par thème
### head-meta — accepté
| ID | Statut | Tier | Action |
|---|---|---|---|
| HM-001 | ❌ → ✅ | auto-safe | appliqué (commit abc1234) |
| HM-002 | ✅ | — | conforme |
| HM-005 | ❌ | manual | TODO dans manual-todos.md |
### robots-crawling — skippé (décision utilisateur : non)
Findings non appliqués listés pour mémoire :
| ID | Statut | Tier |
|---|---|---|
| IX-001 | ❌ | auto-safe |
### sitemap — tout OK
Aucun finding actionnable.
[etc. pour les 11 thèmes]
## Items déjà appliqués (audits précédents)
- HM-006 — appliqué le 2026-04-25 (commit ...)
## Pour aller plus loin
- Items manuels à compléter : voir `.tseo/manual-todos.md`
- Pour ré-auditer après modifs : relance `/seo-audit` (mode incrémental).
- Pour rollback un fix : `git revert <SHA>`.
À la fin de Phase 4, affiche :
recap.md, applied.md, manual-todos.md.php artisan serve et inspecte les pages clefs."Re-run de /seo-audit sur un projet déjà audité :
site_model.json.answers.json existant, ne pose que les nouvelles questions.id + evidence matchent applied.md sont marqués previously_applied et non re-proposés. Si tous les findings d'un thème sont previously_applied, le thème est skippé sans question.recap.md qui isole les nouveaux findings et mentionne séparément ce qui était déjà fait.<project_path> invalide → STOP, demande à l'utilisateur..tseo/ existe ET utilisateur ne veut pas re-runner → STOP, propose de relire <projet>/.tseo/recap.md.Affiche à l'utilisateur :
recap.md, applied.md, manual-todos.mdphp artisan serve. Pour rollback un fix : git revert <SHA>."npx claudepluginhub wengouu/tseo --plugin tseoCreates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.