@seoagent-official/seoagent 1.125.0 → 1.127.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seoagent-official/seoagent",
3
- "version": "1.125.0",
3
+ "version": "1.127.0",
4
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": {
@@ -19,7 +19,7 @@
19
19
  | `cli_technical_fix` | Open technical-SEO issue (meta, schema, canonical, internal linking, …) to fix in a page's source | Safe/reversible |
20
20
  | `cli_new_content` | A content brief with no article written yet — write + publish it | Safe (new content) |
21
21
  | `cli_content_update` | An existing page flagged for revision (declining GSC clicks, low CTR, stale/thin) | Reversible |
22
- | `cli_sitemap_update` | GSC can't fetch a sitemap at `/sitemap.xml` — write/refresh the project's sitemap | Safe |
22
+ | `cli_sitemap_update` | The live sitemap is missing, robots.txt does not point at it, or Google refused it — the file's `reasons` say which | Safe |
23
23
  | `cli_ai_files_update` | The AI-readable file layer (OKF bundle + `llms.txt`) is missing, unmanaged, or stale | Safe |
24
24
  | `cli_new_landing_page` | High-value keyword (`easy_win`/`competitor_gap`) with no page covering it — write a landing page | Safe (new content) |
25
25
  | `cli_draft_ready` | The cloud already wrote a complete article and synced it to `.seoagent/content/<slug>.md` — review and place it | Safe (new content) |
@@ -61,6 +61,7 @@ Start by reading `.seoagent/inbox/README.md` (or `seoagent inbox`) to see the li
61
61
  - **Read the full brief** under `.seoagent/` (briefs file or `strategy/` entry matching `brief_slug`) for the outline, word-count target, and internal-link plan.
62
62
  - Write the article following the skill's content-production protocol (Phase 4), then publish it where this project's content lives (repo `content/` or the connected CMS — you are the publishing engine). Follow the file's go-ahead step: **Standing approval** → publish, commit, ack; otherwise show the user the draft before publishing (interactive sessions can use the visual review loop — `references/draft-review.md`).
63
63
  - **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.
64
+ - **Make sure the new URL is in the site's sitemap.** A framework sitemap built from the content directory usually lists it by itself — check that it does; a hand-kept `sitemap.xml` needs the URL added.
64
65
  - Acknowledge: `seoagent ack <action_id>` (or `--failed --reason "skipped; off-strategy"`).
65
66
 
66
67
  ### `cli_content_update-<id>.md`
@@ -77,11 +78,12 @@ Start by reading `.seoagent/inbox/README.md` (or `seoagent inbox`) to see the li
77
78
 
78
79
  ### `cli_sitemap_update-<id>.md`
79
80
 
80
- - `Read` it. The frontmatter has `action_id` + `sitemap_url`; the body lists the URLs SEOAgent knows (crawled + GSC-discovered — this **includes CMS-hosted blog articles your repo doesn't contain**).
81
- - **Find how the project serves its sitemap** (framework sitemap like Next.js `app/sitemap.ts` / `next-sitemap` / Astro integration, or a static `public/sitemap.xml`, or none yet). Prefer extending the framework sitemap so it stays current.
82
- - **Union** the repo's own routes (which the framework sitemap usually covers) with the URL list in the file (which adds off-repo CMS articles), dedup, and ensure the result is served at `sitemap_url`. Show the user the diff, or under **Standing approval** commit it. Deploy if needed — GSC fetches the live URL. See `references/sitemaps.md` for the generator-detection table.
83
- - **Verify with `seoagent sitemap`** once deployed — it should report 200, no private leakage, and the expected URL count.
84
- - Acknowledge: `seoagent ack <action_id>` (or `--failed --reason "sitemap already served"`). SEOAgent re-submits the sitemap to GSC on its schedule.
81
+ - `Read` it. The frontmatter has `action_id`, `reasons`, `sitemap_url` and `robots_txt_url`. SEOAgent checked the live site before it queued the file, and Search Console is not needed for it. A file without `reasons` comes from an older server and means `missing`.
82
+ - **`missing`** — no working sitemap at a robots.txt `Sitemap:` line, `/sitemap.xml`, `/sitemap-index.xml` or `/sitemap_index.xml`. Find how the project serves its sitemap (framework sitemap like Next.js `app/sitemap.ts` / `next-sitemap` / Astro integration, or a static `public/sitemap.xml`, or none yet); prefer extending the framework sitemap so it stays current. **Union** the repo's routes with the URL list in the file (it adds off-repo CMS articles), dedup, serve it at `sitemap_url`, and add an absolute `Sitemap:` line to robots.txt. See `references/sitemaps.md` for the generator-detection table.
83
+ - **`robots_line`** — the sitemap works, but robots.txt does not point Google at it: no robots.txt, no absolute `Sitemap:` line (a relative `Sitemap: /sitemap.xml` is ignored), or a line naming a file that is gone. Edit robots.txt's **source** (static file or route) and add `Sitemap: <sitemap_url>`. Add no `Disallow` rules as part of this task.
84
+ - **`google`** — Google refused the sitemap on SEOAgent's nightly submit, or counted errors in it; the file quotes Google's answer. Run `seoagent sitemap --url <sitemap_url>`, fix the sitemap's source, and check every `<loc>` is an absolute URL on this domain that answers 200.
85
+ - Show the user the diff, or under **Standing approval** commit it. Deploy — Google reads the live files — then verify with `seoagent sitemap --url <sitemap_url>`.
86
+ - Acknowledge: `seoagent ack <action_id>` (or `--failed --reason "already correct: ..."`). SEOAgent re-checks the live site every autopilot run and withdraws the task by itself once the problem is gone.
85
87
 
86
88
  ### `cli_ai_files_update-<id>.md`
87
89
 
@@ -99,6 +101,7 @@ Start by reading `.seoagent/inbox/README.md` (or `seoagent inbox`) to see the li
99
101
  - Write the article following the content-production protocol (Phase 4 — match the article type's quality rules, add internal links from related cluster pages, etc.). Follow the file's go-ahead step: **Standing approval** → publish, commit, ack; otherwise show the user the draft before publishing (interactive sessions can use the visual review loop — `references/draft-review.md`).
100
102
  - **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.
101
103
  - Publish where this project's content lives (repo `content/` or the connected CMS). Safe (new content). Without **Standing approval**, confirm the user wants this specific page before committing.
104
+ - **Make sure the new URL is in the site's sitemap** (a framework sitemap usually lists it by itself — check; a hand-kept `sitemap.xml` needs it added).
102
105
  - Acknowledge: `seoagent ack <action_id>` (or `--failed --reason "already covered by /existing-page"`).
103
106
 
104
107
  ### `cli_draft_ready-<id>.md`
@@ -106,8 +109,13 @@ Start by reading `.seoagent/inbox/README.md` (or `seoagent inbox`) to see the li
106
109
  - `Read` it. The frontmatter has `action_id`, `article_slug`, `path`, `mode`, and (when drafted from a brief) `brief_slug`. The draft itself is at `.seoagent/<path>`.
107
110
  - **Believe the task's own delivery line, not the word count.** It says either "pulled in the same sync that delivered this task" or "**NOT delivered by this sync**". The second means the pull kept your local copy and the cloud draft never reached disk: read the local file, then run the `seoagent sync --force <path>` the task prints to take the cloud version for that one file. Never publish a file whose body is a `Tracked record` pointer stub — that is the tracking placeholder, not the article.
108
111
  - **Check `mode:` first.** It is the whole contract:
109
- - **`mode: new`** — the slug is free. Review the draft (frontmatter carries title, meta description, status), then place it where this project's content renders: repo-native (mdx_sync) → copy/adapt into the repo's content directory and `seoagent content track` it; CMS → create the entry and track it; cloud-hosted → flip frontmatter `status` to `published` and sync.
112
+ - **`mode: new`** — the slug is free. Review the draft (frontmatter carries title, meta description, status), then place it where this project's content renders: repo-native (mdx_sync) → copy/adapt into the repo's content directory and `seoagent content track` it; CMS → create the entry and track it; cloud-hosted → flip frontmatter `status` to `published` and sync. Then make sure the new URL is in the site's sitemap.
110
113
  - **`mode: revision`** — that slug **already renders live on this site** (`replaces_path` names the file). **Do not place it as a new post and do not overwrite the live file.** `diff` the two, fold in only what is genuinely new or better, and keep the live article's URL, frontmatter and dates. If the draft adds nothing, decline — that is a real answer.
114
+ - **AUTHOR RULE (the byline).** The draft's frontmatter carries `author`, `author_title`, `author_url`, `author_same_as` (and `author_key` once known) only when the site owner set a person author. The task file's **Byline** section has the full rule:
115
+ - **No `author`:** write no author fields and change no template.
116
+ - **The repo's posts already show a byline:** use that convention (the same frontmatter key, the same authors file or registry; `author_key` is how this repo already names the author). Add a missing author to the authors file once.
117
+ - **The posts show no byline:** add one to the post template in the same change: the name linked to `author_url` with `rel="author"`, and the author as a `Person` in the page's Article JSON-LD. Say so in one line of the PR or commit message.
118
+ - Pass `--author-key <the value the post's author field or authors-file entry uses>` to `content track`, so the next draft already knows it. A `mode: revision` draft keeps the live article's byline.
111
119
  - **Match the destination's frontmatter contract, not just the draft's.** The cloud draft carries `title`, `meta_description`, and `status`; the repo's content directory may require fields it does not — an `ogImage`, an `excerpt`, a `date`, a tag list — and may enforce them in CI. Read the content directory's `README`/`_README` and one neighbouring post before writing the file. **Drafts arrive with no images**, so when a hero image is required, produce one in the site's existing style and commit it alongside the post; a draft placed exactly as delivered will otherwise turn the build red.
112
120
  - **Renaming the slug is encouraged** when the generated one reads as a full sentence; a short slug is the better URL. When you rename, pass `--supersedes <the-delivered-slug>` to `content track`. That is what tells the cloud it is the SAME article under a new name — without it the cloud keeps the original as an unfinished draft and delivers it to you all over again.
113
121
  - **A rename has a second half: the draft's own identity fields.** The delivered frontmatter carries the generated `slug:` (and often a `canonical`/`url`), so copying it verbatim into a file you renamed leaves the old slug behind in the body of the post. Loaders commonly resolve identity as `frontmatter.slug || filename` while the **route** resolves from the filename, so the page renders at the new URL and declares a canonical pointing at the old one — a silent self-canonical to a 404, on the page you just chose to publish. Nothing fails loudly. **Drop `slug:` when the destination derives it from the filename**, or update it to the new slug when the destination reads it; do the same for any `canonical`/`url` field. `--supersedes` fixes the cloud's record of the rename, not the repo's.
@@ -96,7 +96,7 @@ A listicle's defining schema is **`ItemList`** (in addition to `Article`). For t
96
96
  {
97
97
  "@type": "Article",
98
98
  "headline": "{Title}",
99
- "author": { "@type": "Person", "name": "{Author}" },
99
+ "author": { "@type": "Person", "name": "{author}", "jobTitle": "{author_title}", "url": "{author_url}", "sameAs": {author_same_as} },
100
100
  "datePublished": "{ISO date}",
101
101
  "dateModified": "{ISO date}",
102
102
  "image": "https://{domain}/{hero}",
@@ -114,6 +114,8 @@ A listicle's defining schema is **`ItemList`** (in addition to `Article`). For t
114
114
  ]
115
115
  ```
116
116
 
117
+ **Author:** take it from the draft's frontmatter: `author` → `name`, `author_title` → `jobTitle`, `author_url` → `url`, `author_same_as` → `sameAs`; leave out a key the draft does not have. No `author` in the frontmatter → use the publisher Organization as the `author`. Never invent a person. The byline on the page follows the same fields (see the AUTHOR RULE in `references/inbox.md`).
118
+
117
119
  Use `itemListOrder` `…OrderAscending`/`…OrderDescending` for a true ranking; omit it (or use unordered) for an unranked "best of". See `references/schema-markup.md` for the full JSON-LD rules.
118
120
 
119
121
  ## Quality rules — a listicle is LIGHTER than a guide/pillar
@@ -88,7 +88,7 @@ twitter:
88
88
  {
89
89
  "@type": "Article",
90
90
  "headline": "{Title}",
91
- "author": { "@type": "Person", "name": "{Author}" },
91
+ "author": { "@type": "Person", "name": "{author}", "jobTitle": "{author_title}", "url": "{author_url}", "sameAs": {author_same_as} },
92
92
  "datePublished": "{ISO date}",
93
93
  "dateModified": "{ISO date}",
94
94
  "image": "https://{domain}/{hero}",
@@ -97,6 +97,8 @@ twitter:
97
97
  ]
98
98
  ```
99
99
 
100
+ **Author:** take it from the draft's frontmatter: `author` → `name`, `author_title` → `jobTitle`, `author_url` → `url`, `author_same_as` → `sameAs`; leave out a key the draft does not have. No `author` in the frontmatter → use the publisher Organization as the `author`. Never invent a person. The byline on the page follows the same fields (see the AUTHOR RULE in `references/inbox.md`).
101
+
100
102
  For "how to" long_tails, add `HowTo`:
101
103
  ```json
102
104
  {
@@ -94,7 +94,7 @@ Pillars almost always need both `Article` and `FAQPage`:
94
94
  {
95
95
  "@type": "Article",
96
96
  "headline": "{Title}",
97
- "author": { "@type": "Person", "name": "{Author Name}" },
97
+ "author": { "@type": "Person", "name": "{author}", "jobTitle": "{author_title}", "url": "{author_url}", "sameAs": {author_same_as} },
98
98
  "datePublished": "{ISO date}",
99
99
  "dateModified": "{ISO date}",
100
100
  "image": "https://{domain}/{hero-image}",
@@ -113,6 +113,8 @@ Pillars almost always need both `Article` and `FAQPage`:
113
113
  ]
114
114
  ```
115
115
 
116
+ **Author:** take it from the draft's frontmatter: `author` → `name`, `author_title` → `jobTitle`, `author_url` → `url`, `author_same_as` → `sameAs`; leave out a key the draft does not have. No `author` in the frontmatter → use the publisher Organization as the `author`. Never invent a person. The byline on the page follows the same fields (see the AUTHOR RULE in `references/inbox.md`).
117
+
116
118
  If the pillar contains step-by-step processes, also add `HowTo` schema. If it contains a glossary section, add `DefinedTermSet`.
117
119
 
118
120
  ## After Writing
@@ -29,7 +29,7 @@ Figure out the destination from the codebase first (you usually already know it
29
29
 
30
30
  The site renders content from files in this repo (Next.js `content/`, Astro `src/content/`, a `_posts/` dir, MDX routes, a static-site generator, etc.).
31
31
 
32
- - **How you publish:** **Read an existing published article first** to learn the exact location, filename convention, and frontmatter shape this site expects. Then write the article **directly into a new file in that same location** (Phase 4 step 7), matching that frontmatter exactly (their field names, their date format, their tags). Inject internal links + image refs. If a route/sitemap entry is needed and missing, add it. **The repo file is the single source of truth for the body** — do NOT also write a full-body copy into `.seoagent/`.
32
+ - **How you publish:** **Read an existing published article first** to learn the exact location, filename convention, and frontmatter shape this site expects. Then write the article **directly into a new file in that same location** (Phase 4 step 7), matching that frontmatter exactly (their field names, their date format, their tags). Inject internal links + image refs. **Byline:** when the draft's frontmatter has `author`, the post carries it the way this repo's posts carry theirs (their author key or authors file); a template with no byline gets one in the same change (the name linked to `author_url` with `rel="author"`, the author `Person` in the Article JSON-LD). No `author` → no author fields. See the AUTHOR RULE in `references/inbox.md`. If a route/sitemap entry is needed and missing, add it. **The repo file is the single source of truth for the body** — do NOT also write a full-body copy into `.seoagent/`.
33
33
  - **Register it so the cloud sees it:** after the repo file is written, run `seoagent content track --slug {slug} --url https://{domain}{blog_path}/{slug} --file {path}` (Phase 4 step 7). That's what makes the article appear on the dashboard — `sync` only walks `.seoagent/`, so a repo-only article is otherwise invisible to the cloud. The track record is a pointer, not a duplicate.
34
34
  - **First post / just-converted blog (no existing file to copy):** if the content dir is empty — a brand-new blog, or one you're converting from a DB/headless source per the guardrail above — define a simple frontmatter convention yourself (`title`, `description`, `date`, `tags`, `slug`) and, if the route doesn't yet read from files, scaffold the loader + route to read the content dir (this is the one-time setup task in "After the user picks", tracked with `setup_status: pending` until it deploys).
35
35
  - **Ship it the way the repo ships:** open a PR (or commit to a branch) so the user's existing CI/CD deploys it. Never push straight to the default branch without asking.
@@ -39,7 +39,7 @@ The site renders content from files in this repo (Next.js `content/`, Astro `src
39
39
 
40
40
  The site pulls content from a CMS. You don't need a SEOAgent adapter — **read how the repo already talks to the CMS** (the existing fetch/SDK code, the env var names) and mirror it to *create* a post.
41
41
 
42
- - **How you publish:** find the CMS client/credentials the app already uses (`.env*`, an SDK import, an API base). Map the article (`title`, `slug`, body, meta, canonical, JSON-LD) to that CMS's content model and create the entry — print the exact `curl`/SDK call for the user to run, or, with explicit consent, run it yourself using their existing credentials. Confirm the post is a draft vs. published per the user's preference. The CMS holds the body; **then `seoagent content track --slug {slug} --url {live-url}` so the dashboard tracks it** (the cloud can't see your CMS).
42
+ - **How you publish:** find the CMS client/credentials the app already uses (`.env*`, an SDK import, an API base). Map the article (`title`, `slug`, body, meta, canonical, the `author` byline when the draft has one, JSON-LD) to that CMS's content model and create the entry — print the exact `curl`/SDK call for the user to run, or, with explicit consent, run it yourself using their existing credentials. Confirm the post is a draft vs. published per the user's preference. The CMS holds the body; **then `seoagent content track --slug {slug} --url {live-url}` so the dashboard tracks it** (the cloud can't see your CMS).
43
43
  - **Mapping starting points:** Strapi → `POST /api/articles` `{data:{…}}`. Sanity → `client.create({_type:'post',…})`. Contentful → Management API `createEntry`. Webflow → `POST /collections/:id/items`. Shopify → `POST /admin/api/.../articles.json`. Ghost → Admin API `posts.add`. WordPress → `POST /wp-json/wp/v2/posts`. For anything unfamiliar, ask the user once how a post gets created, then store the mapping in `project.md` so future articles are one step.
44
44
  - **Best for:** teams with an existing CMS — keep it, just get SEOAgent's content into it.
45
45
 
@@ -30,7 +30,9 @@ JSON-LD is the recommended format. Embed in `<script type="application/ld+json">
30
30
  "author": {
31
31
  "@type": "Person",
32
32
  "name": "Jane Doe",
33
- "url": "https://example.com/authors/jane"
33
+ "jobTitle": "Founder, Acme",
34
+ "url": "https://www.linkedin.com/in/janedoe",
35
+ "sameAs": ["https://www.linkedin.com/in/janedoe", "https://janedoe.com"]
34
36
  },
35
37
  "datePublished": "2026-04-27",
36
38
  "dateModified": "2026-04-27",
@@ -49,6 +51,8 @@ JSON-LD is the recommended format. Embed in `<script type="application/ld+json">
49
51
 
50
52
  Required: `headline`, `author`, `datePublished`, `dateModified`, `image`, `publisher.logo`. Missing any of these and Google may reject the rich result.
51
53
 
54
+ **The author comes from the article's frontmatter**, never from this example: `author` → `name`, `author_title` → `jobTitle`, `author_url` → `url`, `author_same_as` → `sameAs` (SEOAgent's cloud drafts carry these when the site owner added an author). Leave out a key the article does not have. With no `author`, the `author` is the publisher Organization: never invent a person. The visible byline links the name to the same `author_url` with `rel="author"`.
55
+
52
56
  ### FAQPage (add to articles with FAQ sections)
53
57
 
54
58
  ```json
@@ -106,6 +106,12 @@ word_count: 3120
106
106
  status: drafted
107
107
  created_at: 2026-04-27T10:00:00Z
108
108
  brief: tech-seo-guide
109
+ # Byline: present only when the site owner added a person author (cloud drafts carry it).
110
+ author: "Jane Doe"
111
+ author_title: "Founder, Acme"
112
+ author_url: "https://www.linkedin.com/in/janedoe" # the byline link (profile, else website)
113
+ author_same_as: ["https://www.linkedin.com/in/janedoe", "https://janedoe.com"]
114
+ author_key: "jane-doe" # optional: how this repo names the author (learned from `content track --author-key`)
109
115
  images:
110
116
  hero:
111
117
  alt: "Diagram of the technical SEO audit flow from crawl to indexation"
@@ -123,6 +129,8 @@ json_ld:
123
129
  ---
124
130
  ```
125
131
 
132
+ **Byline fields** (`author`, `author_title`, `author_url`, `author_same_as`, `author_key`): the cloud writes them into a draft only when the site owner set a person author (Site settings → Authors, or the `site.authors.set` chat tool). No `author` means the site has no author or uses the company name: write no author fields. When you place the article, put the byline where the repo's own posts keep theirs and fill the Article JSON-LD's author `Person` from these fields (the AUTHOR RULE in `references/inbox.md`).
133
+
126
134
  ## Audit output — `.seoagent/audit/latest.md`
127
135
 
128
136
  ```markdown
@@ -85,7 +85,7 @@ twitter:
85
85
  {
86
86
  "@type": "Article",
87
87
  "headline": "{Title}",
88
- "author": { "@type": "Person", "name": "{Author}" },
88
+ "author": { "@type": "Person", "name": "{author}", "jobTitle": "{author_title}", "url": "{author_url}", "sameAs": {author_same_as} },
89
89
  "datePublished": "{ISO date}",
90
90
  "dateModified": "{ISO date}",
91
91
  "image": "https://{domain}/{hero}",
@@ -98,6 +98,8 @@ twitter:
98
98
  ]
99
99
  ```
100
100
 
101
+ **Author:** take it from the draft's frontmatter: `author` → `name`, `author_title` → `jobTitle`, `author_url` → `url`, `author_same_as` → `sameAs`; leave out a key the draft does not have. No `author` in the frontmatter → use the publisher Organization as the `author`. Never invent a person. The byline on the page follows the same fields (see the AUTHOR RULE in `references/inbox.md`).
102
+
101
103
  Add `HowTo` schema when the article is an action-oriented step-by-step (title starts with "How to..." or has a numbered checklist as the main content).
102
104
 
103
105
  ## After Writing