@seoagent-official/seoagent 1.65.0 → 1.66.1

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.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@seoagent-official/seoagent",
3
- "version": "1.65.0",
4
- "description": "The persistent AI SEO agent for Claude Code. Audits, keyword strategy, briefs, articles, and the autopilot loop (cloud detects → CLI executes → ack closes) — other SEO tools write the prompt, SEOAgent runs it.",
3
+ "version": "1.66.1",
4
+ "description": "The persistent AI SEO agent for Claude Code. Audits, keyword strategy, briefs, articles, real product screenshots from your repo, and the autopilot loop (cloud detects → CLI executes → ack closes) — other SEO tools write the prompt, SEOAgent runs it.",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "seoagent": "index.js"
@@ -48,7 +48,9 @@
48
48
  "keyword-research",
49
49
  "internal-links",
50
50
  "schema-markup",
51
- "autopilot"
51
+ "autopilot",
52
+ "product-screenshots",
53
+ "saas-seo"
52
54
  ],
53
55
  "license": "MIT",
54
56
  "homepage": "https://seoagent.com",
@@ -2,6 +2,20 @@
2
2
 
3
3
  Loaded by Phase 1 (Technical SEO Audit) when running a full audit. The main `SKILL.md` summarizes the categories; this reference holds the complete check list with the exact pattern to match and the recommendation text.
4
4
 
5
+ ## Verify-before-assert (read first — non-negotiable)
6
+
7
+ **Every factual claim about a page's live state must be grounded in an actual fetch of the live URL — never in repo source, memory, or a prior.** Before running these checks, run `seoagent crawl` (Phase 1 Step 0). It writes `.seoagent/audit/evidence.md` — the live-crawl evidence base (exact title, meta, ALL H1s, canonical + server/client-render flag, every JSON-LD `@type`, OG/Twitter tags, the ACTUAL robots.txt contents, the sitemap URL + blog-post counts, client-rendered-shell detection). Read that file and derive Confirmed findings from it.
8
+
9
+ Three rules govern every finding:
10
+
11
+ 1. **Confidence label (mandatory on every finding).** Tag each finding `Confirmed` (verified against the live evidence in `evidence.md` / a fetch you just did), `Likely` (strong inference, not directly verified), or `Hypothesis` (unverified). **Never emit an unverified specific — a price, a line number, a competitor name/roster, a dynamic on-page metric — as a bare fact.** A "2,184 families" style counter that is NOT in the server-fetched HTML is at most `Likely`/`Hypothesis`, never `Confirmed`.
12
+
13
+ 2. **Never recommend adding something that already exists.** If `evidence.md` shows the page already serves `Organization` JSON-LD, a canonical tag, OG tags, etc., do NOT recommend adding them — that's the stale-source hallucination this guardrail exists to stop. Only recommend adding what the live evidence shows is genuinely absent.
14
+
15
+ 3. **Live-vs-source reconciliation.** When repo/source state and the live-rendered page can disagree, grade the finding against the LIVE site. A repo-only issue (e.g. a `noindex` in a source file that isn't in the served HTML, a robots rule in a committed file that the live `/robots.txt` doesn't serve) must be labeled **"repo-only, unconfirmed on live"** and reported separately — never as production reality.
16
+
17
+ **Do not assert repo-internal specifics (file paths, line numbers) as fact in a live-state finding.** They are fixing *hints*, not verified live facts — mark them `Likely` and phrase as "likely in `app/layout.tsx`", never "on line 42".
18
+
5
19
  ## Crawlability & Indexation
6
20
 
7
21
  ### `robots_txt_exists`
@@ -10,9 +24,9 @@ Severity if fail: `high`
10
24
  Recommendation: "Create a robots.txt file at {domain}/robots.txt. At minimum: `User-agent: *\nAllow: /\nSitemap: {domain}/sitemap.xml`."
11
25
 
12
26
  ### `robots_txt_blocks_important_paths`
13
- Check: parse robots.txt for `Disallow:` directives. Match against the URL list in `pages.md`.
27
+ Check: use the ACTUAL robots.txt contents from `evidence.md` (the crawl fetched and parsed the live file). Match its real `Disallow:` rules against the URL list in `pages.md`.
14
28
  Severity: `critical` if blocks > 5 known important pages; `high` if blocks any important page.
15
- Recommendation: List the blocking rules and which pages they affect.
29
+ Recommendation: List **only the blocking rules that actually appear in the fetched robots.txt** and which pages they affect. **Never invent a rule** (e.g. do NOT claim `Disallow: /private/` unless it is verbatim in the fetched file) — quoting a rule that isn't there is the exact hallucination this audit must avoid. If robots.txt wasn't fetched, this check is `Hypothesis` — say "couldn't verify robots.txt" rather than asserting any rule.
16
30
 
17
31
  ### `sitemap_exists`
18
32
  Check: WebFetch `{domain}/sitemap.xml`. Pass = 200 OK and parseable XML.
@@ -67,6 +81,11 @@ Recommendation: "{dependency_url} returned {status}. The pages it powers ({list}
67
81
 
68
82
  > **Why this check matters:** the most common SEO disaster on a live site is a CMS that's been quietly down for weeks — every page returns 200, the audit looks clean, but Google has been deindexing the blog the whole time. This check catches it on the first audit.
69
83
 
84
+ ### `client_rendered_shell`
85
+ Check: `evidence.md` flags the page as a client-rendered shell — the server HTML is near-empty and/or shows a "Loading…" placeholder with no in-body links. The classic case is a blog index that ships a `Loading…` shell with no post `<a>` links in the server HTML: crawlers (and this fetch) see zero posts.
86
+ Severity: `critical` if it's a content-listing / blog-index / indexable page; `high` otherwise.
87
+ Recommendation: "This page renders client-side — the server HTML has no content/links, so crawlers can't see it (Confirmed from the crawl). Server-render or pre-render it (SSR/SSG/ISR) so the {posts/listings} are in the initial HTML. Any on-page data on this page (counts, listings) is Likely/Hypothesis until it's server-rendered."
88
+
70
89
  ## On-Page SEO
71
90
 
72
91
  ### `title_missing`
@@ -105,9 +124,9 @@ Severity: `high`
105
124
  Recommendation: "Add exactly one `<h1>` containing the primary keyword."
106
125
 
107
126
  ### `h1_multiple`
108
- Check: more than one `<h1>` in body.
127
+ Check: `evidence.md` lists more than one `<h1>` for the page (it captures ALL H1s in document order, so conflicting H1s are detected).
109
128
  Severity: `medium`
110
- Recommendation: "Reduce to one `<h1>`. Demote others to `<h2>` or `<h3>`."
129
+ Recommendation: "Page has {N} `<h1>`s ({list}). Reduce to one; demote the others to `<h2>`/`<h3>`."
111
130
 
112
131
  ### `heading_hierarchy_skipped`
113
132
  Check: H1 → H3 with no H2 between, or H2 → H4 with no H3.
@@ -141,6 +160,11 @@ Check: `<img>` tags without `alt=""` attribute (decorative images should have em
141
160
  Severity: `medium` if > 3 images; `low` otherwise.
142
161
  Recommendation: "Add descriptive alt text to {N} images. SEOAgent can write these — `seoagent.js` does it automatically on production."
143
162
 
163
+ ### `missing_product_screenshots`
164
+ Applies only when `project.md` has `site_type: saas` (or the repo renders a real product UI). Check: a landing / feature / how-to page that describes the product but has **no real product screenshot** in its hero or feature sections (illustration-only, stock photo, or no image where a UI shot belongs).
165
+ Severity: `low` (a conversion + trust gap, not an indexation issue).
166
+ Recommendation: "Add a real product screenshot to the hero + feature sections — read `references/screenshots.md` to capture them from the product's own code in this repo. Real UI shots out-convert AI illustrations on a SaaS page."
167
+
144
168
  ### `internal_links_count`
145
169
  Check: number of `<a>` tags pointing to same-domain URLs in body.
146
170
  Severity: `medium` if 0 (orphan); `low` if < 3.
@@ -197,18 +221,20 @@ Recommendation: "Add an FAQ section with 5-7 H3 questions. Powers FAQ schema and
197
221
 
198
222
  ## Schema Markup
199
223
 
224
+ > **Schema checks MUST use `evidence.md`, not WebFetch.** WebFetch strips `<script>` tags — it CANNOT see JSON-LD, so "no schema found" from WebFetch is a false negative that leads directly to recommending schema the page already serves. The `seoagent crawl` evidence base parses raw HTML and lists every JSON-LD `@type` present per page. Read that. **Only flag a schema type as missing when `evidence.md` shows it is genuinely absent — never recommend adding a `@type` that already appears in the evidence.**
225
+
200
226
  ### `no_json_ld_on_article`
201
- Check: page is a blog article (URL pattern `/blog/*`) but page HTML has no `<script type="application/ld+json">` Article schema.
227
+ Check: page is a blog article (URL pattern `/blog/*`) AND `evidence.md` shows **no `Article` (or `BlogPosting`/`NewsArticle`) `@type`** for it.
202
228
  Severity: `medium`
203
- Recommendation: "Add `Article` JSON-LD with headline, author, datePublished, dateModified, image, publisher.logo. See `references/schema-markup.md`."
229
+ Recommendation (only if genuinely absent): "Add `Article` JSON-LD with headline, author, datePublished, dateModified, image, publisher.logo. See `references/schema-markup.md`."
204
230
 
205
231
  ### `no_organization_schema_on_homepage`
206
- Check: homepage has no `Organization` schema.
232
+ Check: `evidence.md` shows the homepage has **no `Organization` `@type`** (and no `SoftwareApplication`/`WebSite` that would already cover the brand entity). If the evidence lists `Organization`/`SoftwareApplication`, this check PASSES — do not recommend adding it.
207
233
  Severity: `medium`
208
- Recommendation: "Add `Organization` JSON-LD on homepage with name, url, logo, sameAs links to social profiles."
234
+ Recommendation (only if genuinely absent): "Add `Organization` JSON-LD on homepage with name, url, logo, sameAs links to social profiles."
209
235
 
210
236
  ### `note_schema_detection_limitation`
211
- WebFetch strips `<script>` tags including JSON-LD. After auditing, always tell the user: "Test your pages at https://search.google.com/test/rich-results for accurate schema validation WebFetch can't see your JSON-LD blocks directly."
237
+ Schema is verified from `evidence.md` (raw-HTML JSON-LD parse), not WebFetch. If the crawl couldn't run, treat any "no schema" conclusion as `Hypothesis` and tell the user: "Test your pages at https://search.google.com/test/rich-results — I couldn't verify JSON-LD via WebFetch (it strips `<script>` blocks). Run `seoagent crawl` for a verified read."
212
238
 
213
239
  ## Site Architecture
214
240
 
@@ -145,6 +145,8 @@ Landing pages need:
145
145
  - 2-4 feature/inline images — actual screenshots, not stock photography
146
146
  - 1 OG image — branded, includes product name and value prop, 1200×630
147
147
 
148
+ **For a SaaS / product site, the hero + feature images should be real product screenshots — read `references/screenshots.md`** for how to capture them from the product's own code in this repo (no Playwright/paid API) and where to place them. Real UI shots out-convert AI illustrations on a SaaS landing page; only fall back to a generated image when a screenshot isn't possible.
149
+
148
150
  In article frontmatter:
149
151
  ```yaml
150
152
  images:
@@ -125,6 +125,7 @@ Same protocol but:
125
125
  - Don't ship the rewrite during a campaign in flight — coordinate with the user
126
126
  - Update OG card and Twitter card alt text (often forgotten)
127
127
  - Update JSON-LD `Product` / `Offer` if pricing or feature claims changed
128
+ - **Screenshot-gap pass (SaaS sites):** during the diagnosis (Step 3), scan the page for hero/feature sections that describe the product but show no real screenshot (illustration-only, stock, or empty). For each gap, capture a real product screenshot from this repo's UI per `references/screenshots.md` and fold it into the rewrite — this is how an *existing* SaaS landing page gets the screenshots it's missing.
128
129
 
129
130
  ### Rewriting Without an Existing Brief
130
131
 
@@ -0,0 +1,83 @@
1
+ # Product Screenshot Protocol
2
+
3
+ Use when the site is a **SaaS / product app** and a page or article would be stronger with a **real screenshot of the product's UI**. A genuine product screenshot is the single highest-converting visual on a SaaS landing page or how-to article — it beats AI illustrations and stock photography every time.
4
+
5
+ **You capture these yourself, from the product's own code in this repo.** No Playwright/Puppeteer dependency, no paid screenshot API — use the screenshot capability already available in your environment (a connected browser/preview tool, or the project's own dev server). If you have no way to capture, fall back gracefully (see **Fallback**) — never block a page on a screenshot.
6
+
7
+ ## When this applies (SaaS gate)
8
+
9
+ Only pursue product screenshots when **both** are true:
10
+
11
+ 1. **The site is a SaaS / product app**, not a content/local/services site. Confirm from the repo, which is the ground truth:
12
+ - `project.md` has `site_type: saas`, **or**
13
+ - the repo renders a real UI (an app shell, dashboard, feature components under `app/`, `src/components/`, etc.) — a product you could screenshot.
14
+ - A plumber's blog, a media site, or a pure-marketing repo with no product UI → **skip this protocol**.
15
+ 2. **A specific spot would be stronger with a UI shot and doesn't have one** (see Gap Evaluation).
16
+
17
+ If the cloud queued an action whose body has a **"Screenshots to capture"** section, the SaaS gate is already a yes — go straight to Gap Evaluation + Capture.
18
+
19
+ ## Where product screenshots add value
20
+
21
+ | Page / section | Shot to capture |
22
+ |---|---|
23
+ | Landing **hero** | The product's primary screen (dashboard, main view) — the "this is the thing" shot |
24
+ | Landing **feature deep-dives** | The specific UI for each feature being described (one shot per feature section) |
25
+ | **How-to / tutorial** article steps | The screen the step is describing — capture the actual state for that step |
26
+ | **Comparison** pages | The product's UI for the capability being compared |
27
+
28
+ Don't over-shoot: 1 hero + 2–4 feature shots is the sweet spot for a landing page. A how-to gets one shot per major step that has a UI.
29
+
30
+ ## Gap Evaluation (new pages AND existing ones)
31
+
32
+ Scan the page for **missing visuals** — places where the reader is asked to imagine UI that you could just show:
33
+
34
+ - A feature `<section>` / feature block describing the product with **no image**.
35
+ - A how-to step ("Click **Connect**", "Open the dashboard") with **no screenshot** of that screen.
36
+ - A landing **hero** with no product shot (or a stock/illustration placeholder where a real screen belongs).
37
+ - Prose like "see the X view", "your dashboard shows…", "the editor lets you…" with nothing to look at.
38
+
39
+ Each gap that passes the SaaS gate is a screenshot to capture. When auditing/refreshing an **existing** landing page, this same scan is how you find screenshots to add — fold the gaps into the rewrite (see `references/rewrite-protocol.md`).
40
+
41
+ ## Capture
42
+
43
+ 1. **Find the route/component** in this repo that renders the UI you want — the page route (e.g. `app/dashboard/page.tsx`, a feature page) or a self-contained component you can render in isolation.
44
+ 2. **Render it.** Detect the project's dev command from `package.json` `scripts` (`dev`, then `start`) and start it (or reuse a server that's already running). Note the local URL (e.g. `http://localhost:3000/<route>`).
45
+ 3. **Capture a PNG** of the rendered route using the screenshot capability available in your environment. Frame/crop to the relevant UI — show the feature, not the whole browser chrome. Use a clean state (seeded/demo data, no personal info, no error toasts).
46
+ - **Do NOT** add Playwright/Puppeteer to the project, and **do NOT** call a paid screenshot API just for this. Use what's already available.
47
+ 4. **Save it** to the repo's public asset directory — detect the convention (`public/screenshots/`, `static/screenshots/`, `assets/`) and create `screenshots/` there if absent. Name it `{page-slug}-{section}.png` (e.g. `pricing-hero.png`, `seo-audit-feature.png`).
48
+ 5. **Reference it** in the page/article with descriptive, keyword-aware alt text and responsive markup that matches how the site embeds images (Next.js `<Image>`, MDX `![alt](path)`, an existing image component — match the site's pattern). Alt text describes the screen + the keyword, e.g. `alt="SEOAgent dashboard showing a completed technical SEO audit with severity badges"`.
49
+
50
+ ## Frontmatter
51
+
52
+ When the page uses `images:` frontmatter (landing pages, content), record the screenshot as a captured asset, not an AI prompt:
53
+
54
+ ```yaml
55
+ images:
56
+ hero:
57
+ alt: "SEOAgent dashboard showing a completed audit with green/red severity badges"
58
+ src: /screenshots/dashboard-hero.png # captured product screenshot (preferred for SaaS)
59
+ inline:
60
+ - alt: "The keyword research view with volume and difficulty columns"
61
+ src: /screenshots/keywords-feature.png
62
+ placement: "after H2 'Keyword research'"
63
+ ```
64
+
65
+ `src` = a real captured screenshot. Fall back to `prompt:` (AI image) only when a screenshot isn't possible.
66
+
67
+ ## Fallback (never block the page)
68
+
69
+ If the product can't be rendered/captured (no dev server, no screenshot tool available, the route needs auth you don't have, or there's no real UI yet):
70
+
71
+ - Leave a marker where the shot belongs so the gap is visible and actionable later:
72
+ ```html
73
+ <!-- SCREENSHOT-TODO: capture the {feature} screen at {route} and place here -->
74
+ ```
75
+ - Also write an AI image `prompt:` into the frontmatter (existing flow in `references/landing-pages.md` → Images) so publishing still succeeds with *some* visual.
76
+ - Mention to the user in one line what you couldn't capture and why, so they can run the dev server / point you at the right screen.
77
+
78
+ ## Conventions
79
+
80
+ - One folder for captures (`public/screenshots/` or the repo's equivalent) — don't scatter PNGs.
81
+ - Real, current UI only — never fabricate a screenshot or reuse a competitor's.
82
+ - No PII / no real customer data in the frame — use demo/seed state.
83
+ - Re-capture when the UI changes materially (a feature redesign stales old shots) — the Gap Evaluation on a refresh catches this.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: seoagent
3
- description: "Persistent AI SEO agent. ALWAYS use when the user mentions: SEO, ranking, rank, keywords, meta tags, search traffic, organic traffic, content strategy, audit, Google, Bing, Search Console, GSC, schema markup, JSON-LD, sitemap, robots.txt, internal linking, AI search, AEO, GEO, OKF, Open Knowledge Format, AI knowledge bundle, llms.txt, alt text, page speed, Core Web Vitals, slug, URL structure, canonical, breadcrumbs, hub and spoke, pillar content, topic cluster, programmatic SEO, landing page SEO, blog SEO, listicle, best-of / top-N roundup, why am I not ranking. Also use SILENTLY when the user asks to write, edit, or improve a blog post, landing page, marketing copy, or any file under content/, posts/, blog/, pages/ — these are SEO-adjacent and should persist to .seoagent/ even without an explicit invocation. Replaces separate seo-audit, content-strategy, programmatic-seo, ai-seo, site-architecture, and schema-markup skills with one unified, persistent workflow."
3
+ description: "Persistent AI SEO agent. ALWAYS use when the user mentions: SEO, ranking, rank, keywords, meta tags, search traffic, organic traffic, content strategy, audit, Google, Bing, Search Console, GSC, schema markup, JSON-LD, sitemap, robots.txt, internal linking, AI search, AEO, GEO, OKF, Open Knowledge Format, AI knowledge bundle, llms.txt, alt text, page speed, Core Web Vitals, slug, URL structure, canonical, breadcrumbs, hub and spoke, pillar content, topic cluster, programmatic SEO, landing page SEO, blog SEO, listicle, best-of / top-N roundup, product screenshots, SaaS screenshots, why am I not ranking. Also use SILENTLY when the user asks to write, edit, or improve a blog post, landing page, marketing copy, or any file under content/, posts/, blog/, pages/ — these are SEO-adjacent and should persist to .seoagent/ even without an explicit invocation. Replaces separate seo-audit, content-strategy, programmatic-seo, ai-seo, site-architecture, and schema-markup skills with one unified, persistent workflow."
4
4
  allowed-tools: Read, Write, Edit, Bash, WebFetch, WebSearch
5
5
  ---
6
6
 
@@ -43,6 +43,7 @@ This SKILL.md is the orchestration layer. Detailed protocols live in `references
43
43
  | Writing a sub-pillar article | `references/sub-pillar-articles.md` |
44
44
  | Writing a long-tail article | `references/long-tail-articles.md` |
45
45
  | Writing a listicle ("Top N" / "Best X") | `references/listicle-articles.md` |
46
+ | Adding **product screenshots** to a SaaS page/article (or a "Screenshots to capture" inbox action) | `references/screenshots.md` |
46
47
  | Programmatic SEO at scale | `references/programmatic.md` |
47
48
  | Adding schema markup / JSON-LD | `references/schema-markup.md` |
48
49
  | Refreshing or rewriting an existing page | `references/rewrite-protocol.md` |
@@ -166,6 +167,7 @@ Offer **once per session per topic**; if declined, drop it and keep working. Nev
166
167
  - `Read` it. The frontmatter has `action_id`, `brief_slug`, `primary_keyword`, `cluster`, and `priority`. The body points at the synced brief.
167
168
  - **Read the full brief** under `.seoagent/` (briefs file or `strategy/` entry matching `brief_slug`) for the outline, word-count target, and internal-link plan.
168
169
  - Write the article following the skill's **content-production protocol** (Phase 4 below), then publish it where this project's content lives (repo `content/` or the connected CMS — you are the publishing engine). Show the user the draft before publishing.
170
+ - **If the action body has a "Screenshots to capture" section** (autopilot flagged this as a SaaS product), follow `references/screenshots.md` — capture real product screenshots from this repo's UI for the relevant sections instead of shipping illustration-only.
169
171
  - Acknowledge it: `seoagent ack <action_id>` (or `--failed --reason "skipped; off-strategy"` to decline).
170
172
 
171
173
  5. For each `cli_content_update-<id>.md` file:
@@ -185,6 +187,7 @@ Offer **once per session per topic**; if declined, drop it and keep working. Nev
185
187
  - Cross-reference `.seoagent/keywords.md` for related keywords — they tell you which cluster this page belongs to and which secondary keywords to weave in.
186
188
  - Pick an article type from `intent` (commercial/transactional → product or comparison page; informational → guide or pillar). Pick a clean URL slug from `keyword`.
187
189
  - Write the article following the skill's **content-production protocol** (Phase 4 — match the article type's quality rules, add internal links from related cluster pages, etc.). Show the user the draft before publishing.
190
+ - **If the action body has a "Screenshots to capture" section** (SaaS product), follow `references/screenshots.md` — a landing page for a SaaS product should lead with a real product screenshot in the hero + feature sections, captured from this repo's UI.
188
191
  - Publish where this project's content lives (repo `content/` or the connected CMS). Safe (new content) — but still confirm the user wants this specific page before committing.
189
192
  - Acknowledge it: `seoagent ack <action_id>` (or `--failed --reason "already covered by /existing-page"` to decline).
190
193
 
@@ -389,9 +392,12 @@ Once approved, work **a cluster at a time**, top of the plan down:
389
392
 
390
393
  ## Phase 1: Technical SEO Audit
391
394
 
392
- ### Step 0 (mandatory)
395
+ ### Step 0 (mandatory — capture the live-crawl evidence base, then read the checks)
393
396
 
394
- **Read `.claude/skills/seoagent/references/audit-checks.md` before any WebFetch.** It contains the full check list with severity tiers and recommendation text per check. Do not run the audit from memorythe reference is the source of truth and gives consistent results across sessions.
397
+ 1. **Run `seoagent crawl` first.** It fetches the homepage + top pages, the real robots.txt, and the live sitemap, and writes `.seoagent/audit/evidence.md` the verified evidence base (exact title/meta, ALL H1s, canonical + server/client-render flag, every JSON-LD `@type`, OG/Twitter tags, the ACTUAL robots.txt contents, sitemap URL + blog-post counts, client-rendered-shell detection). **`Read` that file every `Confirmed` finding must be derived from it, not from repo source or memory.** (Use `seoagent crawl --json` if you want the structured bundle.)
398
+ 2. **Read `.claude/skills/seoagent/references/audit-checks.md`.** It contains the full check list, the **Verify-before-assert** rules (confidence labels, never-recommend-what-exists, live-vs-source reconciliation), severity tiers, and recommendation text per check. Do not run the audit from memory — the reference is the source of truth and gives consistent results across sessions.
399
+
400
+ **Verify-before-assert is the load-bearing rule of the whole audit.** Never state a live-page fact you didn't fetch: don't invent a robots.txt rule, don't recommend adding schema/canonical/OG tags the evidence shows already exist, don't report a dynamic on-page number (a "2,184 families" counter) as `Confirmed` unless it's in the server-fetched HTML. Tag every finding `Confirmed` / `Likely` / `Hypothesis`. If `seoagent crawl` couldn't run (offline, no domain), fall back to per-page WebFetch — but remember WebFetch strips `<script>`, so schema conclusions from it are `Hypothesis`, never `Confirmed`.
395
401
 
396
402
  ### Procedure
397
403
 
@@ -451,7 +457,7 @@ When the user says "I fixed X":
451
457
  2. Append to `changelog.md`: `[date] Fixed: {finding}`.
452
458
  3. Run `seoagent sync`.
453
459
 
454
- > **Rule**: Before reporting any URL is missing or broken, always WebFetch the live URL first. Never assume a 404 from inference alone.
460
+ > **Rule (verify-before-assert)**: Before reporting ANY live-state fact — a URL is missing/broken, robots.txt blocks a path, a page lacks schema, a title/H1/canonical value — it must be grounded in an actual fetch (`seoagent crawl`'s `evidence.md`, or a WebFetch you just ran). Never assume a 404, a robots rule, or a missing schema from inference/repo source alone. Every finding carries a confidence tag (`Confirmed`/`Likely`/`Hypothesis`); unverified specifics (prices, line numbers, competitor names, dynamic counters) are never emitted as bare fact.
455
461
 
456
462
  ---
457
463
 
@@ -737,9 +743,13 @@ This is the per-article procedure. When executing an approved **plan** (see "Pla
737
743
  - **Cloud-hosted (`managed_proxy` / `subdomain`)** — the SEOAgent cloud renders the article, so the body DOES live in `.seoagent/`: write the full article to `.seoagent/content/{slug}.md` with full SEO frontmatter (slug, page_type, title, meta_title, meta_description, canonical, og, twitter, json_ld, images, internal_links) and `seoagent sync`. (No `content track` needed — the full file is the record.)
738
744
  8. **Update the cluster's link graph** — for sub_pillar/long_tail writes, edit the parent (and the cluster file) to add the new link UP. For pillar writes, ensure all sub_pillars are referenced.
739
745
 
746
+ ### Product Screenshots (SaaS — do this before AI images)
747
+
748
+ **If `project.md` has `site_type: saas` (or the repo renders a real product UI), prefer real product screenshots over AI illustrations** — they're the highest-converting visual on a SaaS landing page or how-to article. Before falling back to a generated image, **read `references/screenshots.md`** and follow it: scan the page for spots where a UI shot would add value and is missing (hero, feature sections, how-to steps), then capture those screens **from the product's own code in this repo** (using your environment's screenshot capability + the project's dev server — no Playwright/Puppeteer dependency, no paid API), save them under `public/screenshots/`, and reference them with descriptive alt text. If you can't capture (no dev server / no screenshot tool / no real UI), the protocol's fallback leaves a `<!-- SCREENSHOT-TODO -->` marker + an AI image prompt so publishing still works. Non-SaaS sites skip this and go straight to image generation below.
749
+
740
750
  ### Image Generation (Free Tier)
741
751
 
742
- Always write `images:` frontmatter with `alt` and `prompt`. Then resolve a provider — **don't silently ship imageless articles:**
752
+ Always write `images:` frontmatter with `alt` and `prompt` (or `src` for a captured screenshot — see Product Screenshots above). Then resolve a provider — **don't silently ship imageless articles:**
743
753
 
744
754
  1. **If `project.md` has `image_provider` set to `openai|fal|replicate`**, offer to generate the hero image:
745
755
 
@@ -904,7 +914,7 @@ The CLI manages credentials at `~/.config/seoagent/auth.json` — outside the pr
904
914
  7. **End with the plan's next step, not a menu.** When executing an approved plan, close with progress + what's next in the plan ("3 of 8 in this cluster done; writing the next now"), not a 2–3-option menu every turn. Offer explicit choices only at real decision points (the plan-approval gate, a cluster boundary, an ambiguous call).
905
915
  8. **Update the roadmap and changelog** after every action.
906
916
  9. **Sync after every artifact write.** Run `seoagent sync` (no-op when not logged in — always run it).
907
- 10. **WebFetch before reporting missing.** Never say a URL is missing without fetching it live first.
917
+ 10. **Verify before you assert.** Every claim about a page's live state (robots.txt rules, schema/JSON-LD, meta tags, titles, headings, canonical, sitemap contents, whether a URL exists) must be grounded in an actual live fetch — `seoagent crawl`'s `.seoagent/audit/evidence.md` or a WebFetch you just ran — never repo source, memory, or a prior. **Never recommend adding something the live page already has.** Tag every finding `Confirmed` / `Likely` / `Hypothesis`; never emit an unverified specific (price, line number, competitor, dynamic on-page metric) as a bare fact. Repo-only issues that aren't confirmed on the live site are labeled and reported separately, not as production reality.
908
918
  11. **Use the output template** for all top-level reports.
909
919
  12. **Read context before generating.** Before any strategy, brief, or article, read `.seoagent/context.md`.
910
920
  13. **Plan once, then execute** (see "Plan & Execute"). Get one approval on the content plan, then run it in batches (a cluster at a time) — don't ask `Continue?` between articles or phases. Pause only for: the plan approval, cluster boundaries (show drafts + open a PR), ambiguous decisions, and destructive actions. Go fully autonomous or step-by-step if the user asks.