@softspark/ai-toolkit 2.4.0 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/AGENTS.md +32 -19
  2. package/CHANGELOG.md +45 -0
  3. package/README.md +13 -12
  4. package/app/.claude-plugin/plugin.json +1 -1
  5. package/app/ARCHITECTURE.md +2 -2
  6. package/app/agents/code-reviewer.md +6 -7
  7. package/app/agents/frontend-specialist.md +33 -2
  8. package/app/agents/seo-specialist.md +1 -1
  9. package/app/personas/frontend-lead.md +48 -5
  10. package/app/skills/a11y-validate/SKILL.md +377 -0
  11. package/app/skills/a11y-validate/reference/aria-patterns.md +259 -0
  12. package/app/skills/a11y-validate/reference/eaa-compliance.md +252 -0
  13. package/app/skills/a11y-validate/reference/mobile-eaa.md +329 -0
  14. package/app/skills/a11y-validate/reference/wcag-2-1-aa.md +285 -0
  15. package/app/skills/a11y-validate/reference/wcag-2-2-aa.md +221 -0
  16. package/app/skills/a11y-validate/scripts/a11y-scanner.py +639 -0
  17. package/app/skills/clean-code/reference/python.md +3 -3
  18. package/app/skills/design-engineering/SKILL.md +2 -5
  19. package/app/skills/review/SKILL.md +30 -6
  20. package/app/skills/seo-validate/SKILL.md +460 -0
  21. package/app/skills/seo-validate/reference/core-web-vitals.md +445 -0
  22. package/app/skills/seo-validate/reference/geo-aeo-patterns.md +259 -0
  23. package/app/skills/seo-validate/reference/geo-guidelines.md +248 -0
  24. package/app/skills/seo-validate/reference/schema-types.md +465 -0
  25. package/app/skills/seo-validate/reference/spa-ssg-patterns.md +351 -0
  26. package/app/skills/seo-validate/reference/w3c-guidelines.md +289 -0
  27. package/app/skills/seo-validate/scripts/seo-scanner.py +549 -0
  28. package/bin/ai-toolkit.js +32 -5
  29. package/kb/reference/architecture-overview.md +3 -3
  30. package/kb/reference/cli-reference.md +1 -1
  31. package/kb/reference/codex-cli-compatibility.md +4 -0
  32. package/kb/reference/comparison.md +1 -1
  33. package/kb/reference/extension-api.md +2 -0
  34. package/kb/reference/skills-catalog.md +3 -1
  35. package/llms-full.txt +16 -6
  36. package/manifest.json +3 -3
  37. package/package.json +2 -2
  38. package/scripts/config_cli.py +4 -10
  39. package/scripts/config_resolver.py +23 -5
  40. package/scripts/doctor.py +76 -4
  41. package/scripts/hook_sources.py +3 -0
  42. package/scripts/inject_hook_cli.py +74 -1
  43. package/scripts/install.py +34 -3
  44. package/scripts/install_steps/ai_tools.py +79 -16
  45. package/scripts/install_steps/install_state.py +25 -0
  46. package/scripts/install_steps/markers.py +2 -1
  47. package/scripts/install_steps/project_registry.py +9 -0
  48. package/scripts/plugin.py +1 -1
  49. package/scripts/propagate_global.py +92 -0
  50. package/scripts/rule_sources.py +3 -2
  51. package/scripts/update_projects.py +7 -1
  52. package/scripts/url_fetch.py +5 -0
@@ -24,7 +24,7 @@ Reviews code changes for quality and issues.
24
24
  Before starting manual review, run the diff analyzer script to get a structured risk assessment:
25
25
 
26
26
  ```bash
27
- python3 "$(dirname "$0")/scripts/diff-analyzer.py" [base_branch]
27
+ python3 ${CLAUDE_SKILL_DIR}/scripts/diff-analyzer.py [base_branch]
28
28
  # Default base branch: main
29
29
  # Example: python3 scripts/diff-analyzer.py develop
30
30
  ```
@@ -90,11 +90,35 @@ After all reviewers complete:
90
90
  - [ ] No code duplication
91
91
  - [ ] Appropriate abstractions
92
92
 
93
- ### Security
94
- - [ ] No hardcoded secrets
95
- - [ ] Input validation
96
- - [ ] Proper auth checks
97
- - [ ] SQL injection prevention
93
+ ### Security (OWASP Top 10)
94
+ - [ ] A01: Proper auth/authorization on all endpoints
95
+ - [ ] A02: No weak crypto, HTTPS for external comms
96
+ - [ ] A03: Input validation, parameterized queries, output encoding (XSS)
97
+ - [ ] A04: Threat model assumptions documented for new features
98
+ - [ ] A05: No debug mode, default credentials, or verbose errors in prod config
99
+ - [ ] A06: Dependencies checked for known CVEs
100
+ - [ ] A07: No hardcoded secrets, session management correct
101
+ - [ ] A08: Integrity checks on deserialized data, CI/CD pipeline safety
102
+ - [ ] A09: Security-relevant events logged (without PII)
103
+ - [ ] A10: External URL handling validates scheme/host (SSRF prevention)
104
+
105
+ ### API / Contract Changes
106
+ - [ ] Backward compatibility preserved (no silent breaking changes)
107
+ - [ ] API versioning updated if contract changed
108
+ - [ ] Schema validation on request/response
109
+ - [ ] Error responses follow project convention
110
+
111
+ ### Concurrency / Async
112
+ - [ ] Shared mutable state protected (locks, atomics, channels)
113
+ - [ ] No fire-and-forget promises without error handling
114
+ - [ ] Database transactions scoped correctly (no long-held locks)
115
+ - [ ] Race condition risk assessed for concurrent access paths
116
+
117
+ ### Migrations / Schema Changes
118
+ - [ ] Migration is reversible (has rollback path)
119
+ - [ ] No table locks on large tables during peak hours
120
+ - [ ] Data backfill handles NULL/missing values
121
+ - [ ] Indexes added for new query patterns
98
122
 
99
123
  ### Performance
100
124
  - [ ] No N+1 queries
@@ -0,0 +1,460 @@
1
+ ---
2
+ name: seo-validate
3
+ description: "Validate code against SEO best practices: W3C semantics, meta/OG tags, Schema.org, hreflang, Core Web Vitals (LCP/INP/CLS), resource hints, GEO, SPA/SSG/CSR crawlability, technical SEO, accessibility-for-SEO. Supports React/Next/Nuxt/Astro/Gatsby/SvelteKit/Remix/Angular/Vue/static HTML."
4
+ user-invocable: true
5
+ effort: medium
6
+ disable-model-invocation: true
7
+ context: fork
8
+ agent: seo-specialist
9
+ argument-hint: "[path] [--scope full|technical|content|performance|geo|rendering] [--severity high|warn|info] [--framework auto|next|nuxt|astro|gatsby|sveltekit|remix|angular|vue|react-spa|vite-spa|cra|static] [--rendering auto|csr|ssr|ssg|isr|hybrid] [--output markdown|json]"
10
+ allowed-tools: Read, Grep, Glob, Bash
11
+ ---
12
+
13
+ # /seo-validate — SEO Validation Scanner
14
+
15
+ $ARGUMENTS
16
+
17
+ Scan a codebase for SEO issues using pattern-matching heuristics. Detects W3C/HTML violations, meta tag gaps, structured data problems, hreflang errors, Core Web Vitals risks (LCP/INP/CLS), resource-hint misuse, above-the-fold anti-patterns, GEO gaps, SPA/CSR/SSG crawlability problems, technical SEO misconfigurations, and accessibility-for-SEO issues. Read-only — never modifies files.
18
+
19
+ **Standards basis**: W3C HTML5 Recommendation, W3C WCAG 2.2, Schema.org vocabulary, IETF RFC 5646 (BCP 47 language tags) for hreflang, web.dev Core Web Vitals thresholds (LCP <2.5s, INP <200ms, CLS <0.1), Google Search Central crawlability guidelines, and emerging GEO (Generative Engine Optimization) practices.
20
+
21
+ ## Usage
22
+
23
+ ```
24
+ /seo-validate # Scan full project, auto-detect framework
25
+ /seo-validate src/ # Scan specific path
26
+ /seo-validate --scope rendering # Only SPA/CSR/SSG crawlability checks
27
+ /seo-validate --scope performance # Only Core Web Vitals static signals
28
+ /seo-validate --scope geo # Only GEO (Generative Engine Optimization)
29
+ /seo-validate --severity high # Filter to HIGH findings only
30
+ /seo-validate --framework next # Force framework (skip auto-detection)
31
+ /seo-validate --rendering csr # Force rendering-mode interpretation
32
+ /seo-validate --output json # Structured JSON output for CI integration
33
+ ```
34
+
35
+ **Scopes:**
36
+ - `full` (default) — all 9 categories
37
+ - `technical` — HTML semantics, hreflang, CWV, rendering, technical SEO (categories 1, 4, 5, 7, 8)
38
+ - `content` — meta/OG, structured data, GEO, a11y-for-SEO (categories 2, 3, 6, 9)
39
+ - `performance` — only CWV static signals (category 5)
40
+ - `geo` — only GEO (category 6)
41
+ - `rendering` — only category 7 (SPA/CSR/SSG crawlability) — useful for migration audits
42
+
43
+ **Severity filtering:** `--severity high` shows only HIGH, `--severity warn` shows HIGH+WARN, `--severity info` shows all. Default: all.
44
+
45
+ ## What This Command Does
46
+
47
+ 1. **Detect framework and rendering mode** from `package.json`, config files, and entry HTML.
48
+ 2. **Scan the codebase** using `Grep`/`Glob`/`Read` against framework-aware patterns for each category in scope.
49
+ 3. **Interpret findings** with specific fix suggestions tied to the detected framework.
50
+ 4. **Report** findings with file paths, line numbers, severity, confidence, and standards citations.
51
+
52
+ ## Steps
53
+
54
+ ### Step 1: Detect Framework & Rendering Mode
55
+
56
+ Run detection before scanning so category patterns can adapt. Detection order:
57
+
58
+ 1. **Read `package.json`** (if present) and inspect `dependencies` + `devDependencies`:
59
+
60
+ | Deps contain | Framework | Default rendering |
61
+ |--------------|-----------|-------------------|
62
+ | `next` | `next` | hybrid (per-route) |
63
+ | `nuxt` | `nuxt` | ssr |
64
+ | `astro` | `astro` | ssg |
65
+ | `gatsby` | `gatsby` | ssg |
66
+ | `@sveltejs/kit` | `sveltekit` | hybrid |
67
+ | `@remix-run/*` | `remix` | ssr |
68
+ | `@angular/core` + `@angular/ssr` or `@nguniversal/*` | `angular` | ssr |
69
+ | `@angular/core` alone | `angular` | csr (flag as SPA) |
70
+ | `vue` + `nuxt` | see nuxt row | — |
71
+ | `vue` without `nuxt` | `vue` | csr (flag as SPA) |
72
+ | `react` + `vite` without Next/Remix | `vite-spa` | csr (flag as SPA) |
73
+ | `react-scripts` | `cra` | csr (flag as SPA) |
74
+ | no `package.json` OR no framework deps | `static` | static |
75
+
76
+ 2. **Read config files** to refine:
77
+ - `next.config.*` — check `output: 'export'` (forces SSG), `images`, i18n settings.
78
+ - `nuxt.config.*` — check `ssr: false`, `generate` blocks (SSG export).
79
+ - `astro.config.*` — check `output: 'server'|'static'|'hybrid'` and `prerender` directives.
80
+ - `gatsby-config.*` — plugin list (`gatsby-plugin-react-helmet`, `gatsby-plugin-sitemap`).
81
+ - `svelte.config.*` — adapter choice (`static`, `node`, `vercel`).
82
+ - `vite.config.*` + `package.json` scripts — look for `vite-plugin-ssr`, `vite-plugin-prerender`.
83
+ - `angular.json` — look for SSR builder config.
84
+
85
+ 3. **Read entry HTML** (`public/index.html`, `index.html`, `app/layout.tsx`, `src/app.html`, etc.) to confirm whether meaningful content is prerendered or only a mount point (`<div id="root"></div>`).
86
+
87
+ 4. **Override precedence**: `--framework` and `--rendering` flags override detection.
88
+
89
+ Report the detected framework and rendering mode in the Summary table.
90
+
91
+ ### Step 2: Run Category Scans
92
+
93
+ For each category in `--scope`, apply the pattern set below using `Grep` (for regex across files) and `Read` (for config parsing / ordered checks). Patterns are framework-aware — use the framework detected in Step 1 to select the right rule set.
94
+
95
+ ### Step 3: Interpret and Enrich
96
+
97
+ For each finding:
98
+
99
+ 1. **Read the flagged file/lines** to confirm the match is real (not a comment, not a type-only reference).
100
+ 2. **Add a specific fix** tied to the framework (e.g., "use `next/image` with `priority` prop" vs. "add `<link rel="preload" as="image">` to `<head>`").
101
+ 3. **Mark confidence**: `definitive` for regex matches against known-bad patterns, `heuristic` for co-occurrence / absence checks.
102
+ 4. **Skip false positives** when context shows the concern is addressed elsewhere (e.g., meta tags set in a layout file the route inherits from).
103
+
104
+ ### Step 4: Report
105
+
106
+ Present findings sorted by severity (HIGH → WARN → INFO), then by file path.
107
+
108
+ ---
109
+
110
+ ## Scanner Reference
111
+
112
+ ### Category 1: HTML Semantics & W3C
113
+
114
+ Scan HTML/JSX/Vue/Svelte/Astro templates for W3C HTML5 compliance.
115
+
116
+ | Pattern | Severity | Confidence | Description |
117
+ |---------|----------|------------|-------------|
118
+ | `<html>` without `lang` attribute | HIGH | definitive | HTML5 §3.2.6 — `lang` required for SEO + a11y |
119
+ | Missing `<meta charset="utf-8">` in `<head>` | HIGH | definitive | HTML5 §4.2.5.5 — required first |
120
+ | Missing `<meta name="viewport">` | HIGH | definitive | Mobile-first indexing requires viewport |
121
+ | Multiple `<h1>` per page/route component | WARN | heuristic | One H1 per document is standard SEO practice |
122
+ | No `<h1>` in page component | WARN | heuristic | Every indexable page should have H1 |
123
+ | Heading level skip (h1 → h3) | WARN | heuristic | Document outline breaks assistive tech + crawlers |
124
+ | Missing landmarks (`<main>`, `<nav>`, `<header>`, `<footer>`) | WARN | heuristic | Semantic HTML aids both a11y and crawlers |
125
+ | Missing `<!DOCTYPE html>` | HIGH | definitive | Triggers quirks mode in older browsers |
126
+
127
+ See: [reference/w3c-guidelines.md](reference/w3c-guidelines.md)
128
+
129
+ ---
130
+
131
+ ### Category 2: Meta & Open Graph
132
+
133
+ Check `<head>` composition in entry HTML, framework metadata exports, and route-level metadata.
134
+
135
+ | Pattern | Severity | Confidence | Description |
136
+ |---------|----------|------------|-------------|
137
+ | Missing `<title>` / framework title | HIGH | definitive | Required for SERP display |
138
+ | `<title>` >60 chars OR <10 chars | WARN | definitive | Recommended 50–60 char range |
139
+ | Missing `<meta name="description">` | HIGH | definitive | Required for SERP snippets |
140
+ | Description >160 chars OR <50 chars | WARN | definitive | Recommended 150–160 char range |
141
+ | Missing `<link rel="canonical">` on indexable pages | HIGH | definitive | Prevents duplicate-content dilution |
142
+ | `<meta name="robots" content="noindex">` on production route | WARN | heuristic | Confirm intentional — blocks indexing |
143
+ | Missing OG tags: `og:title`, `og:description`, `og:image`, `og:url`, `og:type` | WARN | definitive | Required for rich social cards |
144
+ | Missing Twitter Card (`twitter:card`) | WARN | definitive | Required for Twitter/X rich previews |
145
+ | OG image without absolute URL | WARN | definitive | OG spec requires absolute URLs |
146
+
147
+ **Framework adapters**:
148
+ - **Next.js App Router**: look for `export const metadata = { ... }` or `generateMetadata()` in `layout.tsx`/`page.tsx`.
149
+ - **Next.js Pages Router**: look for `<Head>` from `next/head`.
150
+ - **Nuxt**: look for `useHead()` / `definePageMeta({ title, ... })`.
151
+ - **Astro**: look for `<BaseHead>` component or direct `<meta>` in layout.
152
+ - **Gatsby**: look for `<Helmet>` from `react-helmet`.
153
+ - **SvelteKit**: look for `<svelte:head>` blocks.
154
+ - **SPAs (Vue/Vite/CRA/Angular)**: look for `react-helmet-async`, `vue-meta`, `@angular/platform-browser`'s `Meta`/`Title` services. Flag runtime-only meta as a rendering-crawlability issue (Category 7).
155
+
156
+ ---
157
+
158
+ ### Category 3: Structured Data / Schema.org
159
+
160
+ Scan for JSON-LD (`<script type="application/ld+json">`) presence and correctness on key page types.
161
+
162
+ | Pattern | Severity | Confidence | Description |
163
+ |---------|----------|------------|-------------|
164
+ | No JSON-LD on article/blog route | WARN | heuristic | `Article` schema improves rich results |
165
+ | JSON-LD missing `@context` | HIGH | definitive | Must be `https://schema.org` |
166
+ | JSON-LD missing `@type` | HIGH | definitive | Type declaration is required |
167
+ | `Article` missing `headline` / `author` / `datePublished` | WARN | definitive | Required properties per schema.org |
168
+ | `FAQPage` missing `mainEntity` array | WARN | definitive | FAQ rich result needs Q&A pairs |
169
+ | `BreadcrumbList` missing `itemListElement` | WARN | definitive | Breadcrumb rich result needs list |
170
+ | `Organization` missing `name` / `url` / `logo` | WARN | definitive | Knowledge Graph signals |
171
+ | `Product` missing `name` / `offers` / `aggregateRating` | WARN | definitive | Product rich results |
172
+ | `LocalBusiness` missing `address` / `telephone` / `openingHours` | WARN | definitive | Local SEO signals |
173
+
174
+ See: [reference/schema-types.md](reference/schema-types.md) for required-property matrix.
175
+
176
+ ---
177
+
178
+ ### Category 4: Hreflang & i18n
179
+
180
+ Scan all locale variants for hreflang correctness.
181
+
182
+ | Pattern | Severity | Confidence | Description |
183
+ |---------|----------|------------|-------------|
184
+ | Hreflang pair not bidirectional (A→B but not B→A) | HIGH | definitive | Google ignores unidirectional hreflang |
185
+ | Missing `hreflang="x-default"` | WARN | definitive | Fallback required for unmatched locales |
186
+ | Missing self-referencing hreflang tag | WARN | definitive | Each version must reference itself |
187
+ | Invalid BCP 47 code (e.g., `en_US` instead of `en-US`) | HIGH | definitive | RFC 5646 requires hyphen-separated subtags |
188
+ | Unknown language code (not ISO 639-1) | HIGH | definitive | Invalid language subtag |
189
+ | Unknown region code (not ISO 3166-1 alpha-2) | HIGH | definitive | Invalid region subtag |
190
+ | Hreflang points to URL returning canonical to different URL | WARN | heuristic | Canonical must match hreflang target |
191
+
192
+ ---
193
+
194
+ ### Category 5: Core Web Vitals (Static Signals)
195
+
196
+ Detect code patterns that cause CWV regressions. Covers LCP, INP, CLS, resource hints, and above-the-fold optimization.
197
+
198
+ #### 5a. LCP (Largest Contentful Paint, target <2.5s)
199
+
200
+ | Pattern | Severity | Confidence | Description |
201
+ |---------|----------|------------|-------------|
202
+ | `<img>` without `width`/`height` attributes | HIGH | definitive | Causes CLS + delays LCP |
203
+ | Above-the-fold `<img>` without `fetchpriority="high"` (or framework equivalent) | HIGH | heuristic | LCP image must be prioritized |
204
+ | Above-the-fold `<img loading="lazy">` | HIGH | definitive | Actively harmful — delays LCP |
205
+ | `@font-face` without `font-display` | HIGH | definitive | Blocks text paint |
206
+ | Missing `<link rel="preload" as="image">` for known hero image | WARN | heuristic | Preload accelerates LCP |
207
+ | Missing `<link rel="preload" as="font" crossorigin>` for self-hosted webfonts | WARN | heuristic | Fonts are a common LCP blocker |
208
+ | Missing `<link rel="preconnect">` for 3rd-party font/image/CDN origins on critical path | WARN | heuristic | Saves ~100–300ms per origin |
209
+ | Render-blocking `<link rel="stylesheet">` without `media` split or critical-inline | WARN | heuristic | Blocks first paint |
210
+ | Responsive image: `<img>` >600px without `srcset`+`sizes` or `<picture>` | WARN | heuristic | Over-fetches on mobile |
211
+ | **Next.js**: `<img>` used instead of `next/image` in route component | WARN | definitive | Misses automatic optimization |
212
+ | **Next.js**: `next/image` without `priority` on detected LCP element | HIGH | heuristic | LCP will under-perform |
213
+ | **Nuxt**: `<img>` instead of `<NuxtImg>`/`<NuxtPicture>` | WARN | definitive | Misses auto-optimization |
214
+ | **Astro**: `<img>` instead of `<Image>` from `astro:assets` | WARN | definitive | Misses auto-optimization |
215
+ | **Gatsby**: `<img>` instead of `GatsbyImage` | WARN | definitive | Misses auto-optimization |
216
+
217
+ #### 5b. INP (Interaction to Next Paint, target <200ms)
218
+
219
+ | Pattern | Severity | Confidence | Description |
220
+ |---------|----------|------------|-------------|
221
+ | `<script>` in `<head>` without `async`/`defer` | HIGH | definitive | Render-blocking |
222
+ | Third-party analytics/chat/ads without `async`/`defer` or framework lazy strategy | WARN | definitive | Blocks main thread |
223
+ | `document.write` usage | HIGH | definitive | Blocks parser; disabled by modern browsers |
224
+ | Heavy top-level `useEffect(() => {...}, [])` (many sync calls) | WARN | heuristic | Long tasks delay INP |
225
+ | Client bundle estimated >300KB gzipped gating interaction | WARN | heuristic | Excessive JS delays hydration + INP |
226
+ | **Next.js**: `<Script>` without `strategy` prop on non-critical scripts | WARN | definitive | Defaults to `afterInteractive` — often not optimal |
227
+ | Missing `fetchpriority="low"` on deferrable below-the-fold resources | INFO | heuristic | Helps browser prioritize LCP |
228
+
229
+ #### 5c. CLS (Cumulative Layout Shift, target <0.1)
230
+
231
+ | Pattern | Severity | Confidence | Description |
232
+ |---------|----------|------------|-------------|
233
+ | Images without `width`/`height` or `aspect-ratio` CSS | HIGH | definitive | Primary CLS cause |
234
+ | Iframes (YouTube/maps/ads) without dimensions or aspect-ratio | HIGH | definitive | Embeds shift layout |
235
+ | Dynamically injected ads/embeds without reserved placeholder space | WARN | heuristic | Shifts layout on load |
236
+ | `@font-face` without `font-display: swap`/`optional` | WARN | definitive | FOIT/FOUT shifts |
237
+ | SSR hydration mismatch: `typeof window` branches rendering different content | WARN | heuristic | Hydration-triggered shift |
238
+ | Skeleton → content of different height | WARN | heuristic | Load-state shift |
239
+
240
+ #### 5d. Resource Hints & Route Prefetching
241
+
242
+ | Pattern | Severity | Confidence | Description |
243
+ |---------|----------|------------|-------------|
244
+ | `<link rel="preload">` for non-critical resource | WARN | heuristic | Wastes bandwidth + contention |
245
+ | Next-route not prefetched when framework supports it (Next `<Link>`, Nuxt `<NuxtLink>`, SvelteKit `data-sveltekit-preload-data`) | INFO | heuristic | Hurts soft-navigation UX |
246
+ | External origin referenced in critical path without `<link rel="preconnect">` | WARN | definitive | Adds 100–300ms per origin |
247
+ | Less-critical external origin without `<link rel="dns-prefetch">` | INFO | heuristic | Lightweight fallback |
248
+ | ESM chunks on critical path without `<link rel="modulepreload">` | INFO | heuristic | Helps browser parse ahead |
249
+ | `<link rel="preload">` appears AFTER resource that uses it in document order | WARN | heuristic | Preload must come first to help |
250
+ | >6 `<link rel="preload">` directives on one page | WARN | heuristic | Over-hinting — browsers throttle |
251
+
252
+ #### 5e. Above-the-Fold Heuristic
253
+
254
+ "Above-the-fold" candidates (confidence: heuristic):
255
+ - First `<img>` / `<Image>` / `<NuxtImg>` / `<Image from 'astro:assets'>` / `GatsbyImage` inside a page/route component.
256
+ - First child of `<main>` or `<section>`.
257
+ - Components named `Hero`, `Banner`, `Masthead`, `Jumbotron`, `HeroSection`, `CoverImage`.
258
+ - Images inside `<header>` that appear before any scroll-margin content.
259
+
260
+ Rules for ATF elements:
261
+ - MUST have explicit `width` + `height`.
262
+ - MUST have high priority (`fetchpriority="high"` or `priority` prop).
263
+ - MUST NOT have `loading="lazy"`.
264
+ - SHOULD have a matching `<link rel="preload">` entry.
265
+
266
+ Rules for below-the-fold:
267
+ - SHOULD have `loading="lazy"` + `decoding="async"`.
268
+ - MAY have `fetchpriority="low"`.
269
+
270
+ See: [reference/core-web-vitals.md](reference/core-web-vitals.md)
271
+
272
+ ---
273
+
274
+ ### Category 6: GEO (Generative Engine Optimization)
275
+
276
+ Content structure for AI answer engines (ChatGPT, Perplexity, Google AI Overviews, Bing Copilot). **All findings here are severity `INFO`** — emerging practice, not penalty-causing.
277
+
278
+ | Pattern | Severity | Confidence | Description |
279
+ |---------|----------|------------|-------------|
280
+ | No `FAQPage` schema on FAQ-style content | INFO | heuristic | Highly extractable by LLMs |
281
+ | No `speakable` schema on summary content | INFO | heuristic | Voice/audio answer engines |
282
+ | Long paragraphs (>400 words) without sub-headings | INFO | heuristic | Harder for LLMs to extract |
283
+ | Missing explicit citation/source markup (`<cite>`, author bylines) | INFO | heuristic | LLM answer engines prefer attributable sources |
284
+ | No `<q>` or quote schema on quoted content | INFO | heuristic | Aids AI extraction |
285
+ | No Q&A structure on how-to content | INFO | heuristic | LLMs favor structured Q&A |
286
+ | Heavy reliance on `<div>` over semantic HTML | INFO | heuristic | Semantic HTML improves AI parsing |
287
+ | Key facts hidden behind JS interactions (tabs, accordions) | INFO | heuristic | LLMs see initial DOM only |
288
+
289
+ See: [reference/geo-guidelines.md](reference/geo-guidelines.md)
290
+
291
+ ---
292
+
293
+ ### Category 7: Rendering Mode & SPA/CSR/SSG Crawlability ⭐
294
+
295
+ **The most critical category for JS apps.** A CSR-only app with no prerendering is effectively invisible to most crawlers.
296
+
297
+ | Pattern | Severity | Confidence | Description |
298
+ |---------|----------|------------|-------------|
299
+ | Entry HTML contains only mount point (`<div id="root">` or `<div id="app">`) with no prerendered content, and no SSR/SSG configured | HIGH | definitive | Crawlers see empty page |
300
+ | Meta/title set only in JS runtime (react-helmet-async, vue-meta, `document.title = ...`) with no SSR/SSG fallback | HIGH | definitive | Public routes won't have crawlable meta |
301
+ | `HashRouter` / hash-based routing (`/#/about`) on public routes | HIGH | definitive | Google ignores fragments for indexing |
302
+ | CSR app without `<noscript>` fallback containing meaningful content | WARN | heuristic | Minimum no-JS signal for crawlers |
303
+ | **Next.js**: `'use client'` at top of every page/layout forcing CSR | WARN | heuristic | Defeats SSR/SSG benefits |
304
+ | **Next.js**: Content page missing `generateMetadata` / static `metadata` export | WARN | heuristic | No crawlable metadata |
305
+ | **Next.js**: `dynamic(..., { ssr: false })` wrapping LCP / above-the-fold content | HIGH | definitive | Blocks both SSR and LCP |
306
+ | **Nuxt**: `ssr: false` in config or route with public content | WARN | heuristic | Disables SSR intentionally |
307
+ | **Astro**: `client:only` on hero/content components | WARN | heuristic | Component not prerendered |
308
+ | **SvelteKit**: `export const ssr = false` on public route | WARN | heuristic | Disables SSR |
309
+ | **Gatsby**: route excluded from prerender (`gatsby-plugin-exclude`) | WARN | heuristic | Verify intent |
310
+ | **Angular SPA**: project uses `@angular/core` without `@angular/ssr` or `@nguniversal/*` | HIGH | definitive | Default Angular is CSR-only |
311
+ | **Vue SPA / React SPA / CRA / Vite-SPA**: no prerender plugin detected (no `vite-plugin-ssr`, `react-snap`, `prerender-spa-plugin`, `vite-plugin-prerender`) | HIGH | definitive | Content invisible to crawlers |
312
+ | `suppressHydrationWarning` overuse (>3 occurrences) | WARN | heuristic | Masks real hydration mismatches |
313
+ | `typeof window !== 'undefined'` / `isBrowser` checks in render paths | WARN | heuristic | Often signals hydration mismatch |
314
+ | Static `robots.txt` references dynamic routes that aren't prerendered | WARN | heuristic | Crawlers hit empty pages |
315
+ | `prerender.io` / `rendertron` / dynamic-rendering middleware detected | INFO | definitive | Legacy pattern — Google now prefers SSR/SSG |
316
+
317
+ See: [reference/spa-ssg-patterns.md](reference/spa-ssg-patterns.md)
318
+
319
+ ---
320
+
321
+ ### Category 8: Technical SEO
322
+
323
+ | Pattern | Severity | Confidence | Description |
324
+ |---------|----------|------------|-------------|
325
+ | Missing `robots.txt` | HIGH | definitive | Blocks crawler directives + sitemap reference |
326
+ | `robots.txt` contains `Disallow: /` in production build | HIGH | definitive | Blocks entire site |
327
+ | Missing `sitemap.xml` / framework sitemap generator | HIGH | definitive | Slows discovery |
328
+ | `robots.txt` missing `Sitemap:` directive | WARN | definitive | Crawlers may not find sitemap |
329
+ | Canonical URLs inconsistent with actual deployed URLs | WARN | heuristic | Dilutes link equity |
330
+ | Canonical URL includes query params on parametrized pages (e.g., `?q=`, `?page=`, `?sort=`) | HIGH | heuristic | Canonical must point to clean base URL, not parametrized variant — else each query variant is a duplicate |
331
+ | Site has search feature (detected: `<input type="search">`, `<form action="/search">`, route `/search`, `?q=` / `?query=` / `?s=` / `?search=`) but `robots.txt` does NOT `Disallow` the search URL pattern | HIGH | heuristic | Parametrized search URLs create unlimited duplicate-content pages — crawl budget waste + index bloat |
332
+ | Site has faceted navigation (filters, sort params, pagination like `?filter=`, `?sort=`, `?page=`, `?color=`) without `robots.txt` Disallow rules OR parameter-handling via canonical | WARN | heuristic | Faceted URLs multiply indexable variants exponentially |
333
+ | Search result page (SRP) missing `<meta name="robots" content="noindex, follow">` | HIGH | heuristic | SRPs are thin/duplicate content per Google Search Essentials; indexing wastes crawl budget |
334
+ | Search result page missing self-referencing canonical OR canonical with dynamic query in it | WARN | heuristic | SRP should either canonical to clean `/search` or be noindexed entirely |
335
+ | Parametrized URLs (tracking: `utm_*`, `gclid`, `fbclid`, `ref=`) served without canonical to clean URL | HIGH | heuristic | Tracking params create duplicate URLs — canonical must strip them |
336
+ | Trailing-slash inconsistency (some pages `/about/`, some `/about`) | WARN | heuristic | Duplicate-content risk |
337
+ | HTTPS not enforced (hardcoded `http://` internal links) | WARN | definitive | Mixed-content + security |
338
+ | No 404 page / no custom `not-found` route | WARN | heuristic | Default 404s hurt UX |
339
+ | Meta `robots: noindex,nofollow` on indexable production routes | HIGH | heuristic | Blocks indexing — verify intent |
340
+
341
+ **Parameter-handling guidance**: Google deprecated the Search Console URL Parameters tool in April 2022. Today the only signals are:
342
+ 1. **Canonical tags** — every parametrized variant must `<link rel="canonical">` to the clean base URL.
343
+ 2. **`robots.txt` Disallow rules** — block crawlers from following parameter patterns entirely (`Disallow: /*?q=*`, `Disallow: /search?*`).
344
+ 3. **`noindex` meta** — allow crawl (for link discovery) but prevent indexing on SRPs and thin faceted pages.
345
+
346
+ Choose ONE strategy per parameter type — mixing `Disallow` + `noindex` is contradictory (Disallow prevents crawler from ever seeing the noindex directive).
347
+
348
+ **Example `robots.txt` for a site with search**:
349
+ ```
350
+ User-agent: *
351
+ Disallow: /search?*
352
+ Disallow: /*?q=*
353
+ Disallow: /*?query=*
354
+ Disallow: /*?s=*
355
+ Disallow: /*?utm_*
356
+ Disallow: /*?gclid=*
357
+ Disallow: /*?fbclid=*
358
+ Allow: /
359
+
360
+ Sitemap: https://example.com/sitemap.xml
361
+ ```
362
+
363
+ **Example canonical on a parametrized page** (`/products?category=shoes&color=red&sort=price`):
364
+ ```html
365
+ <link rel="canonical" href="https://example.com/products">
366
+ ```
367
+
368
+ The canonical points to the clean page; the specific filter combination is a view, not a distinct URL.
369
+
370
+ ---
371
+
372
+ ### Category 9: Accessibility for SEO
373
+
374
+ Accessibility ↔ SEO overlap. WCAG compliance improves ranking signals.
375
+
376
+ | Pattern | Severity | Confidence | Description |
377
+ |---------|----------|------------|-------------|
378
+ | `<img>` missing `alt` attribute | WARN | definitive | WCAG 1.1.1 + image SEO |
379
+ | `<img alt="">` on informational image | WARN | heuristic | Empty alt only for decorative |
380
+ | Icon-only `<button>` without `aria-label` | WARN | definitive | Screen readers + semantic crawlers |
381
+ | Form `<input>` without associated `<label>` | WARN | definitive | WCAG 3.3.2 |
382
+ | `<div>` used for interactive element (click handler on `<div>`) | WARN | heuristic | Should be `<button>` or `<a>` |
383
+ | Link text is "click here" / "read more" | WARN | heuristic | Anchor text is a ranking signal |
384
+ | `<a>` without `href` (fake link) | WARN | definitive | Not crawlable |
385
+
386
+ ---
387
+
388
+ ## Output Format
389
+
390
+ ```markdown
391
+ ## SEO Validation Report
392
+
393
+ ### Summary
394
+ | Metric | Value |
395
+ |--------|-------|
396
+ | Scope | full / technical / content / performance / geo / rendering |
397
+ | Framework detected | next / nuxt / astro / gatsby / sveltekit / remix / angular / vue / react-spa / vite-spa / cra / static |
398
+ | Rendering mode | csr / ssr / ssg / isr / hybrid |
399
+ | Files scanned | N |
400
+ | Public routes found | N |
401
+ | Routes with prerendering | N of N |
402
+ | Findings: HIGH | N |
403
+ | Findings: WARN | N |
404
+ | Findings: INFO | N |
405
+
406
+ ### Findings
407
+
408
+ #### [HIGH] app/layout.tsx:12
409
+ Category: HTML Semantics & W3C
410
+ Confidence: definitive
411
+ Pattern: `<html>` element missing `lang` attribute
412
+ W3C Rule: HTML5 §3.2.6
413
+ Fix: Add `lang="en"` (or appropriate BCP 47 code) to the `<html>` element.
414
+ See: reference/w3c-guidelines.md#lang-attribute
415
+
416
+ #### [HIGH] components/HomeHero.tsx:24
417
+ Category: Core Web Vitals (LCP)
418
+ Confidence: definitive
419
+ Pattern: Above-the-fold `<img>` with `loading="lazy"`
420
+ Rule: LCP anti-pattern — lazy loading the LCP element delays it
421
+ Fix: Remove `loading="lazy"`, add `fetchpriority="high"`. For Next.js use `<Image priority />`.
422
+ See: reference/core-web-vitals.md#above-the-fold
423
+
424
+ #### [HIGH] src/App.tsx:1
425
+ Category: Rendering Mode & SPA Crawlability
426
+ Confidence: definitive
427
+ Pattern: CSR-only React app (Vite) with no prerender plugin
428
+ Rule: Content-site SPAs without SSR/SSG are invisible to most crawlers
429
+ Fix: Add `vite-plugin-ssr` or migrate to Next.js/Remix; OR add `react-snap` for build-time prerender.
430
+ See: reference/spa-ssg-patterns.md#react-spa-migration
431
+ ```
432
+
433
+ **Confidence values**:
434
+ - `definitive` — regex match against a known-bad pattern with high precision.
435
+ - `heuristic` — co-occurrence / absence / ordering / above-the-fold inference — may be false positive.
436
+
437
+ **Exit codes** (when `--output json`):
438
+ - `0` — no HIGH findings.
439
+ - `1` — one or more HIGH findings.
440
+
441
+ ## Rules
442
+
443
+ - **Read-only**: Never modify any files. Report findings only.
444
+ - **Framework-aware**: Always detect framework first; apply the correct rule set.
445
+ - **Standards citation**: Every HIGH/WARN finding must cite a W3C/Schema.org/RFC/web.dev reference.
446
+ - **Skip non-source files**: Binary files, lock files (`package-lock.json`, `yarn.lock`, `pnpm-lock.yaml`), vendored directories (`node_modules/`, `vendor/`, `.git/`, `dist/`, `build/`, `out/`, `.next/`, `.nuxt/`, `.svelte-kit/`, `public/build/`).
447
+ - **No false confidence**: Label heuristic findings clearly; above-the-fold detection is always heuristic.
448
+ - **GEO is INFO-only**: Never flag GEO findings as HIGH/WARN — it is emerging practice.
449
+ - **SPA HIGH bar**: Only flag Category 7 HIGH when the app is clearly a content site (has public routes with meaningful content). Auth-gated apps (dashboards, admin panels) should stay at WARN/INFO since SEO is not a concern.
450
+ - **Noscript is not a substitute for SSR/SSG**: `<noscript>` catches only the "no-JS" case, not the "crawler without JS execution" case — don't upgrade a CSR HIGH to WARN just because noscript exists.
451
+ - **No auto-fix in v1**: Fixing SEO issues requires design/content decisions beyond pattern matching.
452
+
453
+ ## Reference Documents
454
+
455
+ - [reference/w3c-guidelines.md](reference/w3c-guidelines.md) — HTML5 semantic requirements, meta tag specs, language tag rules.
456
+ - [reference/core-web-vitals.md](reference/core-web-vitals.md) — LCP/INP/CLS thresholds, resource hints, above-the-fold heuristic, per-framework image components.
457
+ - [reference/geo-guidelines.md](reference/geo-guidelines.md) — GEO principles, `speakable` schema, citation/source markup, AI-extractable content structure.
458
+ - [reference/geo-aeo-patterns.md](reference/geo-aeo-patterns.md) — AEO (Answer Engine Optimization): `FAQPage`/`HowTo`/`QAPage` schema, `llms.txt`, AI bot `robots.txt` directives, E-E-A-T signals, automated grep patterns for Category 6.
459
+ - [reference/schema-types.md](reference/schema-types.md) — Schema.org JSON-LD templates (Article, FAQ, BreadcrumbList, Organization, Product, LocalBusiness) with required properties.
460
+ - [reference/spa-ssg-patterns.md](reference/spa-ssg-patterns.md) — Rendering-mode decision tree, SPA pitfalls, per-framework detection patterns, prerendering strategies.