@rivi-software/agentic-seo-check 0.0.0-stage → 0.1.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.
Files changed (133) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +210 -2
  3. package/dist/brand.d.ts +11 -0
  4. package/dist/brand.js +22 -0
  5. package/dist/brand.js.map +1 -0
  6. package/dist/cli.d.ts +2 -0
  7. package/dist/cli.js +194 -0
  8. package/dist/cli.js.map +1 -0
  9. package/dist/core/audit.d.ts +72 -0
  10. package/dist/core/audit.js +452 -0
  11. package/dist/core/audit.js.map +1 -0
  12. package/dist/core/concurrency.d.ts +4 -0
  13. package/dist/core/concurrency.js +39 -0
  14. package/dist/core/concurrency.js.map +1 -0
  15. package/dist/core/crawl.d.ts +40 -0
  16. package/dist/core/crawl.js +324 -0
  17. package/dist/core/crawl.js.map +1 -0
  18. package/dist/core/fetch.d.ts +27 -0
  19. package/dist/core/fetch.js +186 -0
  20. package/dist/core/fetch.js.map +1 -0
  21. package/dist/core/html/dom.d.ts +13 -0
  22. package/dist/core/html/dom.js +17 -0
  23. package/dist/core/html/dom.js.map +1 -0
  24. package/dist/core/html/extract.d.ts +10 -0
  25. package/dist/core/html/extract.js +207 -0
  26. package/dist/core/html/extract.js.map +1 -0
  27. package/dist/core/html/structured-data.d.ts +21 -0
  28. package/dist/core/html/structured-data.js +220 -0
  29. package/dist/core/html/structured-data.js.map +1 -0
  30. package/dist/core/html/text.d.ts +23 -0
  31. package/dist/core/html/text.js +152 -0
  32. package/dist/core/html/text.js.map +1 -0
  33. package/dist/core/lighthouse.d.ts +50 -0
  34. package/dist/core/lighthouse.js +216 -0
  35. package/dist/core/lighthouse.js.map +1 -0
  36. package/dist/core/performance-rules.d.ts +3 -0
  37. package/dist/core/performance-rules.js +125 -0
  38. package/dist/core/performance-rules.js.map +1 -0
  39. package/dist/core/render-rules.d.ts +8 -0
  40. package/dist/core/render-rules.js +144 -0
  41. package/dist/core/render-rules.js.map +1 -0
  42. package/dist/core/render.d.ts +22 -0
  43. package/dist/core/render.js +79 -0
  44. package/dist/core/render.js.map +1 -0
  45. package/dist/core/report/markdown.d.ts +2 -0
  46. package/dist/core/report/markdown.js +131 -0
  47. package/dist/core/report/markdown.js.map +1 -0
  48. package/dist/core/robots.d.ts +11 -0
  49. package/dist/core/robots.js +48 -0
  50. package/dist/core/robots.js.map +1 -0
  51. package/dist/core/rules/canonical.d.ts +2 -0
  52. package/dist/core/rules/canonical.js +141 -0
  53. package/dist/core/rules/canonical.js.map +1 -0
  54. package/dist/core/rules/content.d.ts +1 -0
  55. package/dist/core/rules/content.js +55 -0
  56. package/dist/core/rules/content.js.map +1 -0
  57. package/dist/core/rules/document.d.ts +1 -0
  58. package/dist/core/rules/document.js +83 -0
  59. package/dist/core/rules/document.js.map +1 -0
  60. package/dist/core/rules/headings.d.ts +1 -0
  61. package/dist/core/rules/headings.js +81 -0
  62. package/dist/core/rules/headings.js.map +1 -0
  63. package/dist/core/rules/hreflang.d.ts +1 -0
  64. package/dist/core/rules/hreflang.js +100 -0
  65. package/dist/core/rules/hreflang.js.map +1 -0
  66. package/dist/core/rules/images.d.ts +1 -0
  67. package/dist/core/rules/images.js +77 -0
  68. package/dist/core/rules/images.js.map +1 -0
  69. package/dist/core/rules/index.d.ts +14 -0
  70. package/dist/core/rules/index.js +77 -0
  71. package/dist/core/rules/index.js.map +1 -0
  72. package/dist/core/rules/indexing.d.ts +10 -0
  73. package/dist/core/rules/indexing.js +75 -0
  74. package/dist/core/rules/indexing.js.map +1 -0
  75. package/dist/core/rules/keyword.d.ts +1 -0
  76. package/dist/core/rules/keyword.js +111 -0
  77. package/dist/core/rules/keyword.js.map +1 -0
  78. package/dist/core/rules/links.d.ts +1 -0
  79. package/dist/core/rules/links.js +107 -0
  80. package/dist/core/rules/links.js.map +1 -0
  81. package/dist/core/rules/meta.d.ts +1 -0
  82. package/dist/core/rules/meta.js +81 -0
  83. package/dist/core/rules/meta.js.map +1 -0
  84. package/dist/core/rules/response.d.ts +1 -0
  85. package/dist/core/rules/response.js +93 -0
  86. package/dist/core/rules/response.js.map +1 -0
  87. package/dist/core/rules/rule.d.ts +42 -0
  88. package/dist/core/rules/rule.js +51 -0
  89. package/dist/core/rules/rule.js.map +1 -0
  90. package/dist/core/rules/social.d.ts +1 -0
  91. package/dist/core/rules/social.js +115 -0
  92. package/dist/core/rules/social.js.map +1 -0
  93. package/dist/core/rules/structured-data.d.ts +25 -0
  94. package/dist/core/rules/structured-data.js +381 -0
  95. package/dist/core/rules/structured-data.js.map +1 -0
  96. package/dist/core/rules/title.d.ts +1 -0
  97. package/dist/core/rules/title.js +74 -0
  98. package/dist/core/rules/title.js.map +1 -0
  99. package/dist/core/score.d.ts +3 -0
  100. package/dist/core/score.js +17 -0
  101. package/dist/core/score.js.map +1 -0
  102. package/dist/core/site-rules.d.ts +64 -0
  103. package/dist/core/site-rules.js +462 -0
  104. package/dist/core/site-rules.js.map +1 -0
  105. package/dist/core/sitemap.d.ts +13 -0
  106. package/dist/core/sitemap.js +140 -0
  107. package/dist/core/sitemap.js.map +1 -0
  108. package/dist/core/static.d.ts +9 -0
  109. package/dist/core/static.js +281 -0
  110. package/dist/core/static.js.map +1 -0
  111. package/dist/core/summary.d.ts +2 -0
  112. package/dist/core/summary.js +33 -0
  113. package/dist/core/summary.js.map +1 -0
  114. package/dist/core/types.d.ts +296 -0
  115. package/dist/core/types.js +33 -0
  116. package/dist/core/types.js.map +1 -0
  117. package/dist/core/url.d.ts +20 -0
  118. package/dist/core/url.js +191 -0
  119. package/dist/core/url.js.map +1 -0
  120. package/dist/index.d.ts +11 -0
  121. package/dist/index.js +10 -0
  122. package/dist/index.js.map +1 -0
  123. package/dist/mcp-bin.d.ts +2 -0
  124. package/dist/mcp-bin.js +10 -0
  125. package/dist/mcp-bin.js.map +1 -0
  126. package/dist/mcp.d.ts +5 -0
  127. package/dist/mcp.js +380 -0
  128. package/dist/mcp.js.map +1 -0
  129. package/dist/version.d.ts +1 -0
  130. package/dist/version.js +6 -0
  131. package/dist/version.js.map +1 -0
  132. package/package.json +107 -3
  133. package/skills/seo-audit/SKILL.md +53 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 RIVI Software
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,211 @@
1
- # Temporary Holding Version
1
+ <p align="center">
2
+ <a href="https://rivi.software">
3
+ <picture>
4
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/rivi-software/agentic-seo-check/main/assets/rivi-agentic-seo-check-on-dark.svg">
5
+ <img src="https://raw.githubusercontent.com/rivi-software/agentic-seo-check/main/assets/rivi-agentic-seo-check.svg" alt="RIVI agentic-seo-check" width="545">
6
+ </picture>
7
+ </a>
8
+ </p>
2
9
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
10
+ <p align="center">
11
+ SEO analysis built for coding agents: a CLI and an MCP server that audit URLs,<br>
12
+ static build output or raw HTML, and return findings an agent can act on.<br>
13
+ Open source, from <a href="https://rivi.software">RIVI Software</a>.
14
+ </p>
15
+
16
+ <p align="center">
17
+ <a href="https://github.com/rivi-software/agentic-seo-check/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/rivi-software/agentic-seo-check/ci.yml?branch=main&label=CI" alt="CI status"></a>
18
+ <a href="https://www.npmjs.com/package/@rivi-software/agentic-seo-check"><img src="https://img.shields.io/npm/v/@rivi-software/agentic-seo-check?color=0b6b3a" alt="npm version"></a>
19
+ <a href="https://www.npmjs.com/package/@rivi-software/agentic-seo-check"><img src="https://img.shields.io/npm/dm/@rivi-software/agentic-seo-check?color=0b6b3a" alt="Monthly npm downloads"></a>
20
+ <a href="https://github.com/rivi-software/agentic-seo-check/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-0b6b3a" alt="MIT License"></a>
21
+ </p>
22
+
23
+ <p align="center">
24
+ <a href="#install">Install</a>
25
+ ·
26
+ <a href="#cli">CLI</a>
27
+ ·
28
+ <a href="#mcp-server">MCP server</a>
29
+ ·
30
+ <a href="#agent-skill">Agent skill</a>
31
+ ·
32
+ <a href="https://github.com/rivi-software/agentic-seo-check/blob/main/CONTRIBUTING.md">Contributing</a>
33
+ ·
34
+ <a href="https://rivi.software">rivi.software</a>
35
+ </p>
36
+
37
+ <br>
38
+
39
+ agentic-seo-check is one core with two interfaces:
40
+
41
+ - **CLI** `agentic-seo-check` with JSON (default when piped) or Markdown output and CI-friendly exit codes.
42
+ - **MCP server** exposing five tools for Claude Code, Cursor, Codex and any other MCP client.
43
+
44
+ It audits **URLs** (production, staging, `localhost` dev servers), **static build output** (`dist/`, `out/`, `build/`) and **raw HTML**, and returns findings an agent can act on: a stable rule id, severity, what is wrong, the fix, a CSS selector for where it lives, and evidence with the current values.
45
+
46
+ ## What it checks
47
+
48
+ | Area | Examples (123 rules in total, `agentic-seo-check rules` lists them) |
49
+ | --- | --- |
50
+ | On-page basics | title missing/long/short/placeholder ("React App"), meta description, H1 and heading outline, canonical (missing, conflicting, relative, pointing elsewhere), robots meta and `X-Robots-Tag`, `lang`, viewport, charset, doctype |
51
+ | Social | Open Graph completeness, absolute `og:image`, `og:url` vs canonical, Twitter card |
52
+ | Images and links | alt text, filename-style alt, missing dimensions (CLS), empty or generic anchor text, `href="#"`/`javascript:` anchors, http links on https pages, internal nofollow |
53
+ | International | hreflang syntax, absolute URLs, self reference, x-default, return links (crawl) |
54
+ | Structured data | JSON-LD parse errors, `@context`/`@type`, required and recommended properties for Article, Product, Offer, Organization, WebSite, BreadcrumbList, FAQPage, LocalBusiness, Event, Recipe, VideoObject, JobPosting, HowTo, Review, SoftwareApplication and more, ISO 8601 dates; microdata too |
55
+ | Content | empty or thin content, "lorem ipsum", readability, keyword presence in title/H1/body/description/URL and density |
56
+ | Response | 4xx/5xx, redirects and chains, slow HTML response, non-HTML content type, http instead of https |
57
+ | Site-level (crawl or directory) | robots.txt (missing, blocking, disallowed pages), sitemap (missing, invalid, noindex/non-canonical/broken/redirecting URLs, pages not in sitemap, orphan pages), broken internal links with their sources, redirected links and chains, duplicate titles/descriptions/H1s/content, deep pages, soft 404, http→https and www redirects, hreflang return links |
58
+ | Rendering (`--render`, Playwright) | title/description/canonical/H1/content/links/structured data that only exist after JavaScript runs; noindex injected or removed by JavaScript; console errors |
59
+ | Performance (`--lighthouse`, Chrome) | LCP, CLS, TBT, FCP, Speed Index, TTFB with Core Web Vitals thresholds, Lighthouse opportunities with estimated savings, failed Lighthouse SEO and agentic-browsing audits |
60
+
61
+ ## Install
62
+
63
+ Requires Node 22.19 or newer. Published on npm as [`@rivi-software/agentic-seo-check`](https://www.npmjs.com/package/@rivi-software/agentic-seo-check); the commands it installs are `agentic-seo-check` and `agentic-seo-check-mcp`.
64
+
65
+ ```bash
66
+ npx @rivi-software/agentic-seo-check audit https://example.com # no install
67
+ npm install -g @rivi-software/agentic-seo-check # or pnpm add -D / npm i -D in a project
68
+ agentic-seo-check --help
69
+ ```
70
+
71
+ Optional, only for `--render` and `--lighthouse`: `playwright` and `lighthouse` (+ `chrome-launcher`) are optional peer dependencies, so install them next to the tool, plus Google Chrome or `npx playwright install chromium`. The renderer uses a Playwright-managed Chromium when present and falls back to the installed Google Chrome or Edge.
72
+
73
+ From source: `pnpm install && pnpm build && node dist/cli.js --help`.
74
+
75
+ ## CLI
76
+
77
+ ```bash
78
+ # one page (Markdown on a terminal, JSON when piped or with --format json)
79
+ agentic-seo-check audit https://www.example.com/pricing
80
+
81
+ # whole site
82
+ agentic-seo-check crawl https://www.example.com --max-pages 100 --max-depth 4
83
+
84
+ # local dev server, ignore the dev robots.txt, include JS rendering comparison
85
+ agentic-seo-check audit http://localhost:3000 --crawl --ignore-robots --render
86
+
87
+ # static export, with the production origin for canonical/sitemap comparison
88
+ agentic-seo-check audit ./out --base-url https://www.example.com
89
+
90
+ # raw HTML from stdin
91
+ cat page.html | agentic-seo-check audit - --base-url https://www.example.com/page
92
+
93
+ # keyword focus, Lighthouse, Markdown report to a file
94
+ agentic-seo-check audit https://www.example.com/widgets --keyword "blue widgets" --lighthouse -o report.md
95
+
96
+ # CI: exit 1 when errors exist, smaller output
97
+ agentic-seo-check audit https://staging.example.com --crawl --fail-on error --compact --no-info
98
+
99
+ # rule reference
100
+ agentic-seo-check rules --category structured-data
101
+ agentic-seo-check rules --rule canonical.multiple
102
+ ```
103
+
104
+ Exit codes: `0` success, `1` findings at or above `--fail-on`, `2` usage or runtime error. Progress goes to stderr, the report to stdout.
105
+
106
+ ## MCP server
107
+
108
+ Add to a project `.mcp.json` (Claude Code reads it automatically):
109
+
110
+ ```json
111
+ {
112
+ "mcpServers": {
113
+ "agentic-seo-check": {
114
+ "command": "npx",
115
+ "args": ["-y", "@rivi-software/agentic-seo-check", "mcp"]
116
+ }
117
+ }
118
+ }
119
+ ```
120
+
121
+ Or `claude mcp add agentic-seo-check -- npx -y @rivi-software/agentic-seo-check mcp`. From a source checkout, use `node /absolute/path/to/agentic-seo-check/dist/mcp-bin.js` instead (this repo's own `.mcp.json` does that).
122
+
123
+ | Tool | Purpose |
124
+ | --- | --- |
125
+ | `seo_audit_page` | Audit one URL or an HTML string. Options: `keyword`, `render`, `lighthouse`, `formFactor`, `includeInfo`, `disableRules`, `headers`, `format` (json/markdown), `detail` (compact/full). |
126
+ | `seo_crawl_site` | Crawl and audit a site. Options: `maxPages`, `maxDepth`, `concurrency`, `delayMs`, `respectRobots`, `includeSubdomains`, `checkExternalLinks`, `includePatterns`, `excludePatterns`, `pageDetail`. |
127
+ | `seo_audit_directory` | Audit built HTML files in a directory. Options: `dir`, `baseUrl`, `maxPages`, `pageDetail`. |
128
+ | `seo_lighthouse` | Lighthouse performance + SEO + agentic-browsing for one URL. |
129
+ | `seo_list_rules` | Rule reference, filter by `category` or `rule`. |
130
+
131
+ All tools are read-only. Results are returned both as text (JSON or Markdown) and as `structuredContent`.
132
+
133
+ ## Agent skill
134
+
135
+ `skills/seo-audit/SKILL.md` teaches an agent the audit → fix → re-audit loop, how to map rule ids to framework code (Next.js, Astro, Nuxt, SvelteKit, Remix, TanStack Start), and the caveats (preview-host noindex, localhost limits). Copy it to `.claude/skills/seo-audit/` in a project or `~/.claude/skills/` to make it available; this repo links it under `.claude/skills/`.
136
+
137
+ ## Output shape
138
+
139
+ ```jsonc
140
+ {
141
+ "schemaVersion": 1,
142
+ "kind": "page", // or "site"
143
+ "url": "https://www.example.com/",
144
+ "score": 67, // 100 - 15/error - 5/warning - 1/info, floored at 0
145
+ "counts": { "error": 0, "warning": 6, "info": 3 },
146
+ "summary": { "title": "…", "metaDescription": null, "canonical": null, "indexable": true, "h1": [], "wordCount": 25, "...": "…" },
147
+ "findings": [
148
+ {
149
+ "rule": "meta-description.missing",
150
+ "severity": "warning",
151
+ "category": "meta",
152
+ "message": "Page has no meta description.",
153
+ "recommendation": "Add a unique meta description (about 70-160 characters) …",
154
+ "selector": "head > meta[name=\"description\"]",
155
+ "url": "https://www.example.com/",
156
+ "docs": "https://developers.google.com/search/docs/appearance/snippet"
157
+ }
158
+ ],
159
+ "response": { "status": 200, "finalUrl": "…", "redirectChain": [], "timeMs": 142, "headers": { "content-type": "text/html" } },
160
+ "performance": { "metrics": { "performanceScore": 99, "lcpMs": 848, "cls": 0, "...": "…" }, "opportunities": [] }, // with --lighthouse
161
+ "rendering": { "raw": { "wordCount": 25 }, "rendered": { "wordCount": 137 }, "...": "…" } // with --render
162
+ }
163
+ ```
164
+
165
+ Site audits add `crawl` stats, `robotsTxt`, `sitemaps`, `siteFindings`, `issues` (page findings rolled up per rule with example URLs) and `pages`.
166
+
167
+ ## Library
168
+
169
+ ```ts
170
+ import { auditUrl, auditSite, auditDirectory, auditHtml, renderMarkdown } from '@rivi-software/agentic-seo-check';
171
+
172
+ const page = await auditUrl('https://www.example.com/', { keyword: 'example', render: true });
173
+ const site = await auditSite('https://www.example.com/', { maxPages: 100 });
174
+ console.log(renderMarkdown(site));
175
+ ```
176
+
177
+ ## Scope of this first version
178
+
179
+ - Crawls are breadth-first, same-site, HTML only, and obey robots.txt by default. `--render`/`--lighthouse` apply to the start page only during a crawl.
180
+ - Scores are indicative. Thresholds (title 30-60 chars, description 70-160, thin content 300 words, slow response 2 s, deep page depth 3) are configurable through the library `thresholds` option.
181
+ - Not included yet: gzip sitemaps, rendering every page in a crawl, field (CrUX) data, rank tracking, backlink data, AI-generated copy suggestions.
182
+
183
+ ## Contributing
184
+
185
+ Bug reports, wrong findings, new rules and fixes are welcome from anyone. With Node 24 and pnpm 12:
186
+
187
+ ```bash
188
+ pnpm install
189
+ pnpm run verify
190
+ ```
191
+
192
+ `verify` runs what CI runs: format, lint, typecheck, tests, build and a check of the packed package. `pnpm run dev audit https://example.com` runs the CLI from source. [CONTRIBUTING.md](https://github.com/rivi-software/agentic-seo-check/blob/main/CONTRIBUTING.md) covers proposing a change, what a pull request needs and how releases reach npm, and [AGENTS.md](https://github.com/rivi-software/agentic-seo-check/blob/main/AGENTS.md) is the brief for coding agents working on the tool itself. Everyone who takes part follows the [code of conduct](https://github.com/rivi-software/agentic-seo-check/blob/main/CODE_OF_CONDUCT.md). [SECURITY.md](https://github.com/rivi-software/agentic-seo-check/blob/main/SECURITY.md) says how to report a vulnerability privately.
193
+
194
+ ## License
195
+
196
+ Licensed under the [MIT License](https://github.com/rivi-software/agentic-seo-check/blob/main/LICENSE). Copyright © 2026 RIVI Software.
197
+
198
+ <br>
199
+
200
+ <p align="center">
201
+ <a href="https://rivi.software">
202
+ <picture>
203
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/rivi-software/agentic-seo-check/main/assets/rivi-mark-on-dark.svg">
204
+ <img src="https://raw.githubusercontent.com/rivi-software/agentic-seo-check/main/assets/rivi-mark.svg" alt="RIVI Software" width="36" height="36">
205
+ </picture>
206
+ </a>
207
+ </p>
208
+
209
+ <p align="center">
210
+ <sub>RIVI agentic-seo-check is made by <a href="https://rivi.software">RIVI Software</a>, which builds software for radio stations. Created in <a href="https://t3.codes">T3 Code</a>.</sub>
211
+ </p>
@@ -0,0 +1,11 @@
1
+ /**
2
+ * RIVI Software's name, site and mark as the CLI, the MCP server and the Markdown report show
3
+ * them. The marks are copies of assets/rivi-mark.svg and assets/rivi-mark-on-dark.svg, inlined
4
+ * because npm ships dist/ without assets/.
5
+ */
6
+ import type { Icon } from "@modelcontextprotocol/sdk/types.js";
7
+ export declare const MAKER = "RIVI Software";
8
+ export declare const MAKER_URL = "https://rivi.software";
9
+ export declare const REPOSITORY_URL = "https://github.com/rivi-software/agentic-seo-check";
10
+ /** The mark as MCP icons: the ink tile for light themes, the light tile for dark ones. */
11
+ export declare const MCP_ICONS: Icon[];
package/dist/brand.js ADDED
@@ -0,0 +1,22 @@
1
+ export const MAKER = "RIVI Software";
2
+ export const MAKER_URL = "https://rivi.software";
3
+ export const REPOSITORY_URL = "https://github.com/rivi-software/agentic-seo-check";
4
+ const MARK_SVG = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40" role="img" aria-label="RIVI Software"><rect width="40" height="40" rx="9" fill="#15171a"/><g fill="#f5f6f7"><rect x="8" y="16" width="3" height="8" rx="1.5"/><rect x="13.25" y="11" width="3" height="18" rx="1.5"/><rect x="18.5" y="7" width="3" height="26" rx="1.5" fill="#3be08a"/><rect x="23.75" y="12" width="3" height="16" rx="1.5"/><rect x="29" y="16" width="3" height="8" rx="1.5"/></g></svg>';
5
+ const MARK_ON_DARK_SVG = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40" role="img" aria-label="RIVI Software"><rect width="40" height="40" rx="9" fill="#f5f6f7"/><g fill="#15171a"><rect x="8" y="16" width="3" height="8" rx="1.5"/><rect x="13.25" y="11" width="3" height="18" rx="1.5"/><rect x="18.5" y="7" width="3" height="26" rx="1.5" fill="#0e9f55"/><rect x="23.75" y="12" width="3" height="16" rx="1.5"/><rect x="29" y="16" width="3" height="8" rx="1.5"/></g></svg>';
6
+ const dataUri = (svg) => `data:image/svg+xml;base64,${Buffer.from(svg, "utf-8").toString("base64")}`;
7
+ /** The mark as MCP icons: the ink tile for light themes, the light tile for dark ones. */
8
+ export const MCP_ICONS = [
9
+ {
10
+ mimeType: "image/svg+xml",
11
+ sizes: ["any"],
12
+ src: dataUri(MARK_SVG),
13
+ theme: "light",
14
+ },
15
+ {
16
+ mimeType: "image/svg+xml",
17
+ sizes: ["any"],
18
+ src: dataUri(MARK_ON_DARK_SVG),
19
+ theme: "dark",
20
+ },
21
+ ];
22
+ //# sourceMappingURL=brand.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"brand.js","sourceRoot":"","sources":["../src/brand.ts"],"names":[],"mappings":"AAOA,MAAM,CAAC,MAAM,KAAK,GAAG,eAAe,CAAC;AACrC,MAAM,CAAC,MAAM,SAAS,GAAG,uBAAuB,CAAC;AAEjD,MAAM,CAAC,MAAM,cAAc,GACzB,oDAAoD,CAAC;AAEvD,MAAM,QAAQ,GACZ,2cAA2c,CAAC;AAE9c,MAAM,gBAAgB,GACpB,2cAA2c,CAAC;AAE9c,MAAM,OAAO,GAAG,CAAC,GAAW,EAAE,EAAE,CAC9B,6BAA6B,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;AAE9E,0FAA0F;AAC1F,MAAM,CAAC,MAAM,SAAS,GAAW;IAC/B;QACE,QAAQ,EAAE,eAAe;QACzB,KAAK,EAAE,CAAC,KAAK,CAAC;QACd,GAAG,EAAE,OAAO,CAAC,QAAQ,CAAC;QACtB,KAAK,EAAE,OAAO;KACf;IACD;QACE,QAAQ,EAAE,eAAe;QACzB,KAAK,EAAE,CAAC,KAAK,CAAC;QACd,GAAG,EAAE,OAAO,CAAC,gBAAgB,CAAC;QAC9B,KAAK,EAAE,MAAM;KACd;CACF,CAAC"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,194 @@
1
+ #!/usr/bin/env node
2
+ import { promises as fs } from "node:fs";
3
+ import { Command, InvalidArgumentError, Option } from "commander";
4
+ import { MAKER, MAKER_URL, REPOSITORY_URL } from "./brand.js";
5
+ import { auditTarget, compactAudit } from "./core/audit.js";
6
+ import { renderMarkdown } from "./core/report/markdown.js";
7
+ import { ALL_RULES } from "./core/rules/index.js";
8
+ import { SEVERITY_RANK } from "./core/types.js";
9
+ import { startMcpServer } from "./mcp.js";
10
+ import { VERSION } from "./version.js";
11
+ const program = new Command();
12
+ program
13
+ .name("agentic-seo-check")
14
+ .description("SEO analysis for coding agents. Audits URLs, static build output or raw HTML and prints machine-readable findings.")
15
+ .version(VERSION)
16
+ .addHelpText("afterAll", `\nMade by ${MAKER} (${MAKER_URL}). Docs and issues: ${REPOSITORY_URL}`)
17
+ .showHelpAfterError();
18
+ const intArg = (value) => {
19
+ // oxlint-disable-next-line unicorn/prefer-number-coercion -- parseInt reads the leading digits ("10.5" is 10), where Number would refuse what users have always passed
20
+ const n = Number.parseInt(value, 10);
21
+ if (!Number.isFinite(n) || n < 0) {
22
+ throw new InvalidArgumentError("Expected a non-negative integer.");
23
+ }
24
+ return n;
25
+ };
26
+ const addAuditOptions = (cmd, crawlDefault) => {
27
+ if (!crawlDefault) {
28
+ cmd.option("--crawl", "Crawl the whole site starting at the URL instead of auditing one page");
29
+ }
30
+ return cmd
31
+ .option("--max-pages <n>", "Maximum pages to crawl", intArg, 50)
32
+ .option("--max-depth <n>", "Maximum link depth from the start URL", intArg, 3)
33
+ .option("--concurrency <n>", "Parallel requests while crawling", intArg, 5)
34
+ .option("--delay <ms>", "Delay between requests per worker", intArg, 0)
35
+ .option("-k, --keyword <keyword>", "Target keyword for keyword.* checks")
36
+ .option("--render", "Render with headless Chromium (Playwright) and compare with the server HTML")
37
+ .option("--lighthouse", "Run Lighthouse for Core Web Vitals and Lighthouse SEO audits (slow)")
38
+ .addOption(new Option("--form-factor <type>", "Lighthouse device emulation")
39
+ .choices(["mobile", "desktop"])
40
+ .default("mobile"))
41
+ .option("--base-url <url>", "Origin used for directories, files and stdin HTML (default http://localhost/)")
42
+ .option("--ignore-robots", "Do not obey robots.txt while crawling")
43
+ .option("--include-subdomains", "Treat subdomains as internal when crawling")
44
+ .option("--check-external", "Probe external links for 4xx/5xx while crawling")
45
+ .option("--include <regex...>", "Only crawl URLs matching these patterns")
46
+ .option("--exclude <regex...>", "Skip URLs matching these patterns")
47
+ .addOption(new Option("-f, --format <format>", "Output format (default: json when piped, md on a terminal)").choices(["json", "md"]))
48
+ .option("-o, --out <file>", "Write the report to a file instead of stdout")
49
+ .addOption(new Option("--fail-on <severity>", "Exit with code 1 when findings at or above this severity exist")
50
+ .choices(["error", "warning", "info", "none"])
51
+ .default("none"))
52
+ .option("--no-info", "Omit info-level findings")
53
+ .option("--compact", "Trim evidence lists and bulky fields (smaller output for LLM consumption)")
54
+ .addOption(new Option("--page-detail <level>", "Per-page detail in site reports")
55
+ .choices(["full", "summary"])
56
+ .default("full"))
57
+ .option("--disable <rule...>", "Rule ids to skip")
58
+ .option("--timeout <ms>", "Per-request timeout", intArg, 15_000)
59
+ .option("--user-agent <ua>", "Custom User-Agent header")
60
+ .option("-H, --header <header...>", 'Extra request header "Name: value" (repeatable)')
61
+ .option("-q, --quiet", "Suppress progress output on stderr");
62
+ };
63
+ /** The report format: as asked, else by the output file's extension, else Markdown on a terminal. */
64
+ const formatOf = (opts) => {
65
+ if (opts.format !== undefined) {
66
+ return opts.format;
67
+ }
68
+ if (opts.out) {
69
+ return opts.out.endsWith(".md") ? "md" : "json";
70
+ }
71
+ return process.stdout.isTTY ? "md" : "json";
72
+ };
73
+ const runAudit = async (target, opts, forceCrawl) => {
74
+ const headers = {};
75
+ for (const h of opts.header ?? []) {
76
+ const idx = h.indexOf(":");
77
+ if (idx === -1) {
78
+ throw new InvalidArgumentError(`Header "${h}" must be "Name: value".`);
79
+ }
80
+ headers[h.slice(0, idx).trim()] = h.slice(idx + 1).trim();
81
+ }
82
+ const showProgress = !opts.quiet && process.stderr.isTTY;
83
+ const onProgress = (e) => {
84
+ if (!showProgress) {
85
+ return;
86
+ }
87
+ const counter = e.crawled === undefined ? "" : ` [${e.crawled}/${e.discovered}]`;
88
+ process.stderr.write(`\u001B[2m${e.phase}${counter}: ${e.message}\u001B[0m\n`);
89
+ };
90
+ const options = {
91
+ crawl: forceCrawl || Boolean(opts.crawl),
92
+ maxPages: opts.maxPages,
93
+ maxDepth: opts.maxDepth,
94
+ concurrency: opts.concurrency,
95
+ delayMs: opts.delay,
96
+ keyword: opts.keyword,
97
+ render: opts.render,
98
+ lighthouse: opts.lighthouse,
99
+ formFactor: opts.formFactor,
100
+ baseUrl: opts.baseUrl,
101
+ respectRobots: !opts.ignoreRobots,
102
+ includeSubdomains: opts.includeSubdomains,
103
+ checkExternalLinks: opts.checkExternal,
104
+ includePatterns: opts.include,
105
+ excludePatterns: opts.exclude,
106
+ includeInfo: opts.info,
107
+ disableRules: opts.disable,
108
+ pageDetail: opts.pageDetail,
109
+ fetch: {
110
+ timeoutMs: opts.timeout,
111
+ userAgent: opts.userAgent,
112
+ headers: Object.keys(headers).length ? headers : undefined,
113
+ },
114
+ onProgress,
115
+ };
116
+ let audit = await auditTarget(target, options);
117
+ if (opts.compact) {
118
+ audit = compactAudit(audit);
119
+ }
120
+ const format = formatOf(opts);
121
+ const output = format === "md" ? renderMarkdown(audit) : JSON.stringify(audit, null, 2);
122
+ if (opts.out) {
123
+ await fs.writeFile(opts.out, `${output}\n`, "utf-8");
124
+ if (!opts.quiet) {
125
+ process.stderr.write(`Report written to ${opts.out}\n`);
126
+ }
127
+ }
128
+ else {
129
+ process.stdout.write(`${output}\n`);
130
+ }
131
+ if (opts.failOn !== "none") {
132
+ const threshold = SEVERITY_RANK[opts.failOn];
133
+ const { counts } = audit;
134
+ const hits = ["error", "warning", "info"]
135
+ .filter((s) => SEVERITY_RANK[s] >= threshold)
136
+ .reduce((n, s) => n + counts[s], 0);
137
+ if (hits > 0) {
138
+ if (!opts.quiet) {
139
+ process.stderr.write(`${hits} finding(s) at or above "${opts.failOn}" severity.\n`);
140
+ }
141
+ process.exitCode = 1;
142
+ }
143
+ }
144
+ };
145
+ addAuditOptions(program
146
+ .command("audit")
147
+ .argument("<target>", 'URL, directory of HTML files, .html file, or "-" for HTML on stdin')
148
+ .description("Audit a page, a site (--crawl), a build directory or raw HTML"), false).action(async (target, opts) => {
149
+ await runAudit(target, opts, false);
150
+ });
151
+ addAuditOptions(program
152
+ .command("crawl")
153
+ .argument("<url>", "Start URL")
154
+ .description('Crawl a site and audit every page (same as "audit --crawl")'), true).action(async (url, opts) => {
155
+ await runAudit(url, opts, true);
156
+ });
157
+ program
158
+ .command("rules")
159
+ .description("List the rules this tool can report")
160
+ .option("--category <category>", "Filter by category")
161
+ .option("--rule <id>", "Show one rule")
162
+ .addOption(new Option("-f, --format <format>", "Output format")
163
+ .choices(["json", "md"])
164
+ .default(process.stdout.isTTY ? "md" : "json"))
165
+ .action((opts) => {
166
+ const rules = ALL_RULES.filter((r) => (!opts.category || r.category === opts.category) &&
167
+ (!opts.rule || r.id === opts.rule));
168
+ if (opts.format === "json") {
169
+ process.stdout.write(`${JSON.stringify({ count: rules.length, rules }, null, 2)}\n`);
170
+ return;
171
+ }
172
+ const lines = [
173
+ "| Rule | Severity | Category | Checks |",
174
+ "| --- | --- | --- | --- |",
175
+ ];
176
+ for (const r of rules) {
177
+ lines.push(`| \`${r.id}\` | ${r.severity} | ${r.category} | ${r.description.replaceAll("|", "\\|")}${r.requires ? ` _(requires ${r.requires})_` : ""} |`);
178
+ }
179
+ process.stdout.write(`${lines.join("\n")}\n`);
180
+ });
181
+ program
182
+ .command("mcp")
183
+ .description("Start the MCP server on stdio (for Claude Code, Cursor, Codex, etc.)")
184
+ .action(async () => {
185
+ await startMcpServer();
186
+ });
187
+ try {
188
+ await program.parseAsync(process.argv);
189
+ }
190
+ catch (error) {
191
+ process.stderr.write(`agentic-seo-check: ${error instanceof Error ? error.message : String(error)}\n`);
192
+ process.exit(2);
193
+ }
194
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,72 @@
1
+ import type { ProgressEvent } from "./crawl.js";
2
+ import type { FetchOptions } from "./fetch.js";
3
+ import type { Audit, IssueRollup, PageAudit, PageSource, ResponseInfo, SiteAudit, Thresholds } from "./types.js";
4
+ export type { ProgressEvent } from "./crawl.js";
5
+ export interface AuditOptions {
6
+ /** Target keyword/phrase for the keyword.* rules. */
7
+ keyword?: string;
8
+ /** Render with headless Chromium (Playwright) and compare against the server HTML. */
9
+ render?: boolean;
10
+ /** Run Lighthouse (performance + SEO + agentic-browsing categories). */
11
+ lighthouse?: boolean;
12
+ formFactor?: "mobile" | "desktop";
13
+ /** Include info-level findings (default true). */
14
+ includeInfo?: boolean;
15
+ /** Rule ids to skip. */
16
+ disableRules?: string[];
17
+ thresholds?: Partial<Thresholds>;
18
+ fetch?: FetchOptions;
19
+ /** Base URL for inline HTML, files and directories (default http://localhost/). */
20
+ baseUrl?: string;
21
+ includeSubdomains?: boolean;
22
+ onProgress?: (event: ProgressEvent) => void;
23
+ }
24
+ export interface CrawlOptions extends AuditOptions {
25
+ maxPages?: number;
26
+ maxDepth?: number;
27
+ concurrency?: number;
28
+ delayMs?: number;
29
+ respectRobots?: boolean;
30
+ checkExternalLinks?: boolean;
31
+ maxLinkChecks?: number;
32
+ includePatterns?: string[];
33
+ excludePatterns?: string[];
34
+ /** How much per-page detail to keep in the site result. */
35
+ pageDetail?: "full" | "summary";
36
+ }
37
+ export interface HtmlAuditInput {
38
+ url?: string;
39
+ response?: ResponseInfo;
40
+ source?: PageSource;
41
+ target?: string;
42
+ depth?: number;
43
+ }
44
+ export declare const DEFAULT_CRAWL: {
45
+ readonly maxPages: 50;
46
+ readonly maxDepth: 3;
47
+ readonly concurrency: 5;
48
+ readonly delayMs: 0;
49
+ readonly maxLinkChecks: 200;
50
+ };
51
+ export declare const rollupIssues: (pages: PageAudit[]) => IssueRollup[];
52
+ export declare const recomputeSite: (site: SiteAudit) => void;
53
+ /** Audit an HTML string. `url` is used to resolve links and for display. */
54
+ export declare const auditHtml: (html: string, input?: HtmlAuditInput, options?: AuditOptions) => Promise<PageAudit>;
55
+ /** Fetch and audit one URL. Optionally render and/or run Lighthouse. */
56
+ export declare const auditUrl: (url: string, options?: AuditOptions) => Promise<PageAudit>;
57
+ /** Audit a single .html file from disk. */
58
+ export declare const auditFile: (filePath: string, options?: AuditOptions) => Promise<PageAudit>;
59
+ /** Crawl a site and audit every page plus site-level checks. */
60
+ export declare const auditSite: (startUrl: string, options?: CrawlOptions) => Promise<SiteAudit>;
61
+ /** Audit a directory of built HTML files (dist/, out/, build/). */
62
+ export declare const auditDirectory: (dir: string, options?: CrawlOptions) => Promise<SiteAudit>;
63
+ /**
64
+ * Auto-detect the target type: http(s) URL, directory, .html file, or "-" for stdin.
65
+ * `crawl` forces a site crawl for URLs.
66
+ */
67
+ export declare const auditTarget: (target: string, options?: CrawlOptions & {
68
+ crawl?: boolean;
69
+ stdinHtml?: string;
70
+ }) => Promise<Audit>;
71
+ /** Drop bulky, low-value fields to save tokens when the result is consumed by an LLM. */
72
+ export declare const compactAudit: <T extends Audit>(audit: T) => T;