ai-search-auditor 0.1.0 → 0.1.2

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/CHANGELOG.md CHANGED
@@ -1,11 +1,25 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.1.2](https://github.com/a-bibikov/ai-search-audit/compare/v0.1.1...v0.1.2) (2026-11-03)
4
+
5
+
6
+ ### New checks and features
7
+
8
+ * checklist by section at the end of the report ([c43423c](https://github.com/a-bibikov/ai-search-audit/commit/c43423c6f1c522de056552980c39db7cb411b082))
9
+
10
+ ## [0.1.1](https://github.com/a-bibikov/ai-search-audit/compare/v0.1.0...v0.1.1) (2026-11-03)
11
+
12
+
13
+ ### Documentation
14
+
15
+ * what the audit checks, roadmap, release process ([365965a](https://github.com/a-bibikov/ai-search-audit/commit/365965a7f2f0842a4dba13a0d1b8f1dfa8a8404b))
16
+
3
17
  ## 0.1.0 — 2026-11-03
4
18
 
5
19
  First release, published to npm as `ai-search-auditor` (commands `ai-search-auditor` and `ai-search-audit`).
6
20
 
7
21
  - CLI `ai-search-audit <url>`: checks whether ChatGPT, Claude, Perplexity and other answer engines can read, understand and cite a site.
8
- - 18 rules in seven groups: access for AI agents (A), text without JavaScript (B), entities and authorship (C), answer first (D), machine-readable versions such as `llms.txt` (E), freshness (F), content safety (G).
22
+ - 20 checks in seven groups: access for AI agents (A), text without JavaScript (B), entities and authorship (C), answer first (D), machine-readable versions such as `llms.txt` (E), freshness (F), content safety (G).
9
23
  - Score 0–100 with a grade; a rule counts once, not per page.
10
24
  - Local builds with `--base`, page sampling across templates with `--max-pages`, CI exit codes with `--fail-on`.
11
25
  - `llms-txt` subcommand: checks only `llms.txt` and `llms-full.txt`.
package/README.md CHANGED
@@ -10,6 +10,12 @@ No sign-up, no API keys, no dependencies. Node.js 22.18+.
10
10
 
11
11
  The npm package is `ai-search-auditor`; once installed (`npm i -g ai-search-auditor`), the command is also available as `ai-search-audit`.
12
12
 
13
+ ## Why this matters now
14
+
15
+ People increasingly get answers from ChatGPT, Claude, Perplexity and Google AI Overviews instead of a list of links. An answer engine reads a handful of pages, picks a few sources and cites them. The rest of the web does not exist for that answer.
16
+
17
+ Whether your site is among those sources depends on things classic SEO tools rarely look at: whether AI agents are let in, whether the text is there without JavaScript, whether the model can tell who the author is, whether the answer sits where it gets quoted. A site can rank well in Google and still be invisible, misread or skipped by AI search. Most of these problems are a few lines of code to fix, once you know they are there.
18
+
13
19
  ## Why another GEO checker
14
20
 
15
21
  Most GEO / AI SEO checkers score a checklist: "is there an FAQ?", "are AI bots named in robots.txt?". They miss what breaks citations in practice and raise alarms that are not problems. This one reads the site the way a model does:
@@ -22,18 +28,48 @@ Most GEO / AI SEO checkers score a checklist: "is there an FAQ?", "are AI bots n
22
28
 
23
29
  ## What it checks
24
30
 
25
- | Group | Rules |
26
- |---|---|
27
- | **A. Access** | A-1 search agents (OAI-SearchBot, ChatGPT-User, Claude-SearchBot, PerplexityBot, Googlebot, Bingbot…) are allowed in robots.txt · A-2 training crawlers follow your policy · A-3 the server answers AI agents like a browser (no 403, no challenge) · A-4 no `noindex` / `noai` · A-5 the feed is declared in `<head>` |
28
- | **B. Text without JS** | B-1 the text is in the HTML · B-2 no invisible characters in title, meta, JSON-LD, feeds, `llms.txt` · B-3 headings do not glue words together |
29
- | **C. Entities** | C-1 Person or Organization with `@id`, `sameAs`, `jobTitle` · C-2 articles point at the author by `@id` · C-4 complete Article / BlogPosting · C-6 og:title, og:description, og:image · C-7 a visible FAQ is marked up |
30
- | **D. Answer first** | D-2 articles answer in the first two paragraphs |
31
- | **E. Machine-readable** | E-1 `llms.txt` follows llmstxt.org · E-2 its links work and match the sitemap · E-3 `llms.txt` per language · E-5 `llms-full.txt` carries the full text |
32
- | **F. Freshness** | F-1 sitemap `lastmod` matches `dateModified` |
33
- | **G. Content safety** | G-1 no instructions addressed to AI in the content |
31
+ 20 checks in seven groups.
32
+
33
+ **A. Access for AI agents**
34
+ - **A-1** AI search agents are allowed to read the site
35
+ - **A-2** Training crawlers are allowed or blocked the way you intend
36
+ - **A-3** The server answers AI agents the same way it answers a browser
37
+ - **A-4** Public pages are not marked as closed to indexing or AI
38
+ - **A-5** The news feed can be found from the pages
39
+
40
+ **B. Text without JavaScript**
41
+ - **B-1** The content is in the page itself, not only rendered by scripts
42
+ - **B-2** Titles, metadata, structured data, feeds and `llms.txt` are free of invisible characters that split words
43
+ - **B-3** Headings read as separate words, not glued together
44
+
45
+ **C. Entities and authorship**
46
+ - **C-1** The author or company is described as one clear entity with profiles
47
+ - **C-2** Articles are linked to that author
48
+ - **C-4** Articles carry complete markup: headline, dates, author, image, language
49
+ - **C-6** Every page has a link card: title, description, image
50
+ - **C-7** A visible FAQ is marked up as one
51
+
52
+ **D. Answer first**
53
+ - **D-2** Articles give the answer in the first two paragraphs
54
+
55
+ **E. Machine-readable versions**
56
+ - **E-1** `llms.txt` exists and follows the standard
57
+ - **E-2** Every link in `llms.txt` works and is a published page
58
+ - **E-3** Each language has its own `llms.txt`
59
+ - **E-5** `llms-full.txt` carries the full text
60
+
61
+ **F. Freshness**
62
+ - **F-1** The sitemap and the page agree on when it was last updated
63
+
64
+ **G. Content safety**
65
+ - **G-1** The content has no instructions addressed to AI
66
+
67
+ Gaps in the numbering are taken by the checks on the [roadmap](#roadmap).
34
68
 
35
69
  Levels: `error` — AI search cannot read the page or reads it wrong; `warn` — lowers the chance of a citation; `info` — a recommendation.
36
70
 
71
+ The report lists the findings with fixes first, then ends with a checklist of every check by group: ✓ OK, ✖ error, ⚠ warning, ℹ note.
72
+
37
73
  **Score:** 100 minus 15 per rule with an error and 5 per rule with a warning. A rule counts once, not per page, so one template bug does not sink a large site. A ≥ 90, B ≥ 80, C ≥ 70, D ≥ 60, F below.
38
74
 
39
75
  ## Usage
@@ -98,18 +134,112 @@ The report goes to the job summary.
98
134
 
99
135
  | | Before | After |
100
136
  |---|---|---|
101
- | Score | **60/100 (D)** | **95/100 (A)** |
137
+ | Score | **60/100 (D)** | **100/100 (A)** |
102
138
  | C-1 Person without `@id` on home and CV | ✖ | ✓ |
103
139
  | C-2 Articles do not point at the author by `@id` | ⚠ | ✓ |
104
140
  | B-3 H1 reads as «Aleksandr BibikovI build…» | ⚠ | ✓ |
105
141
  | B-2 Invisible U+2060 in RSS (32 + 56) | ⚠ | ✓ (kept in on-page typography, ℹ) |
106
142
  | G-1 «A note for AI agents…» in `llms.txt` | ⚠ | ✓ |
107
- | C-6 No og:image on home, CV, blog | ⚠ | ⚠ |
143
+ | C-6 No og:image on home, CV, blog | ⚠ | ✓ |
108
144
 
109
- Each fix was a few lines: a stable `@id`, a `{" "}` between two spans, stripping word joiners from the feed, deleting one line from `llms.txt`.
145
+ Most fixes were a few lines: a stable `@id`, a `{" "}` between two spans, stripping word joiners from the feed, deleting one line from `llms.txt`. The link cards took one image generator at build time.
110
146
 
111
147
  A score-based checker gave the same site 90/100 before the fixes and missed all five problems, while flagging an FAQ that does not exist.
112
148
 
149
+ ## Roadmap
150
+
151
+ Each check ships with tests on a site where it must fire and one where it must stay silent.
152
+
153
+ ### 0.2 — next
154
+
155
+ **L. Server logs** — a new command, `ai-search-auditor logs <access.log>`: what AI bots really do on your site, not what they could do
156
+ - **L-1** Which AI bots visit the site and which pages they read
157
+ - **L-2** How many of their requests end in errors, redirects or blocks
158
+ - **L-3** Important pages no AI bot has ever seen
159
+ - **L-4** Fake bots: an AI user agent coming from an address its operator does not use
160
+
161
+ **A. Access**
162
+ - **A-8** Every known AI bot — training, search and user-triggered — is allowed or blocked the way you intend
163
+ - **A-9** Opting out of Gemini training does not silently remove the site from Gemini answers
164
+ - **A-10** robots.txt, Content Signals, meta tags and `llms.txt` do not contradict each other
165
+ - **A-11** Bot protection does not show AI agents a challenge page or rate-limit them
166
+ - **A-12** Missing pages answer «not found», not a normal page that gets indexed and quoted
167
+
168
+ **D. Answer first**
169
+ - **D-6** Each section makes sense on its own, so it can be quoted without the rest of the page
170
+
171
+ **I. Ready for AI agents** — agents now fill in forms and place orders for people
172
+ - **I-1** Forms and buttons can be used by an agent acting for a person
173
+ - **I-2** Key actions — contact, sign-up, checkout — do not stop an agent at the first step
174
+ - **I-3** The site tells agents where its API or MCP server is, if it has one
175
+
176
+ ### Later
177
+
178
+ **A. Access**
179
+ - **A-6** Paywalls, cookie walls and pop-ups do not hide the text from crawlers
180
+ - **A-7** Pages answer fast enough and stay within the size crawlers read
181
+ - **A-13** Redirect chains are short
182
+ - **A-14** AI agents are not blocked by country
183
+ - **A-15** Signed agent requests are accepted
184
+ - **A-16** The EU text-and-data-mining reservation is stated where you need it
185
+
186
+ **B. Text without JavaScript**
187
+ - **B-4** The page after scripts run says the same as the raw HTML
188
+ - **B-5** Images have alt text and captions; data is not locked inside pictures
189
+ - **B-6** The main text is not drowned in menus, footers and banners
190
+ - **B-7** Text behind tabs, accordions and «show more» is reachable
191
+ - **B-8** Content is not hidden in frames or drawn on a canvas
192
+ - **B-9** Linked PDFs and documents contain real text
193
+
194
+ **C. Entities and authorship**
195
+ - **C-3** The about or CV page is marked up as a profile
196
+ - **C-5** Blog and listing pages are marked up as collections of articles
197
+ - **C-8** The company has a logo and contacts; a local business has one consistent name, address and phone
198
+ - **C-9** Product pages carry price, availability and ratings
199
+ - **C-10** Author profiles linked from the site are alive and link back
200
+ - **C-11** Structured data says the same as the visible text: FAQ, prices, dates
201
+ - **C-12** The company is described the same way on every page and profile
202
+ - **C-13** The site has about and contact pages
203
+ - **C-14** The company or author is known to Wikidata
204
+ - **C-15** Authors have their own page with their experience
205
+
206
+ **D. Answer first**
207
+ - **D-1** The first paragraph contains the page's main query
208
+ - **D-3** The publication and update dates are visible and match the markup
209
+ - **D-4** Content is easy to quote: question headings, lists, comparison tables
210
+ - **D-5** Facts are citable: numbers come with sources and dates
211
+ - **D-7** The page defines its subject and states a short conclusion
212
+
213
+ **E. Machine-readable versions**
214
+ - **E-4** Pages are available as Markdown
215
+ - **E-6** New pages are announced to search engines as they appear (IndexNow; ChatGPT search relies on Bing)
216
+
217
+ **F. Freshness**
218
+ - **F-2** Feeds carry the full text and correct dates
219
+ - **F-3** No stale signals: last year in a title, an «updated» date long ago
220
+
221
+ **G. Content safety**
222
+ - **G-2** No hidden text stuffed with keywords
223
+ - **G-3** No instructions to AI hidden in image text, comments or invisible elements
224
+ - **G-4** AI bots get the same page as people
225
+
226
+ **H. Languages**
227
+ - **H-1** Language versions point at each other correctly and do not compete as duplicates
228
+
229
+ ### Beyond the checks
230
+ - **Score per answer engine.** ChatGPT, Claude and Perplexity read raw HTML, Gemini and Apple run scripts: one site can be visible to some and invisible to others
231
+ - **Fact check.** Ask the models what they know about you and compare it with the site: «ChatGPT says you do not ship to Turkey, your site says you do»
232
+ - **Visibility monitor.** What ChatGPT, Claude and Perplexity answer to your queries, who they cite instead of you, and how it changes week to week
233
+ - **Site profiles:** blog, shop, SaaS, local business — each gets only the checks that matter for it
234
+ - **CI that reports only new problems,** with a file of accepted exceptions
235
+ - **SARIF output,** so findings show up in GitHub code scanning
236
+ - **Fixes for your stack:** ready snippets for Next.js, WordPress, Astro; a Claude Code skill and an MCP server that apply them
237
+ - **Online version** with no install at [albibikov.com/tools/ai-search-audit](https://albibikov.com/tools/ai-search-audit)
238
+ - **HTML report** to share with people who do not read Markdown
239
+ - **More report languages:** see below
240
+
241
+ Ideas are welcome in [issues](https://github.com/a-bibikov/ai-search-audit/issues); for pull requests see [CONTRIBUTING.md](CONTRIBUTING.md).
242
+
113
243
  ## Need the fixes, not just the report?
114
244
 
115
245
  The audit shows what blocks AI search. If you want it fixed and measured — what ChatGPT, Claude and Perplexity answer about your niche today and 30 days later — see [AI search audit on albibikov.com](https://albibikov.com/tools/ai-search-audit).
package/dist/i18n/en.d.ts CHANGED
@@ -15,8 +15,17 @@ export declare const en: {
15
15
  }) => string;
16
16
  clean: string;
17
17
  fix: string;
18
+ /** Label instead of «Fix» when a rule has only notes: nothing is broken, it is advice. */
19
+ recommendation: string;
18
20
  more: (n: number) => string;
19
- passed: string;
21
+ checklist: string;
22
+ legend: string;
23
+ /** Findings of one rule in the checklist: «2 errors, 1 warning». */
24
+ count: (o: {
25
+ errors: number;
26
+ warnings: number;
27
+ notes: number;
28
+ }) => string;
20
29
  site: string;
21
30
  footer: (pages: number, date: string) => string;
22
31
  groups: {
package/dist/i18n/en.js CHANGED
@@ -8,8 +8,19 @@ export const en = {
8
8
  summary: (o) => `**${o.score}/100 (${o.grade})** · ${o.errors} errors · ${o.warnings} warnings · ${o.notes} notes · ${o.pages} pages`,
9
9
  clean: "Nothing to fix: AI search can read, understand and cite these pages.",
10
10
  fix: "Fix",
11
+ /** Label instead of «Fix» when a rule has only notes: nothing is broken, it is advice. */
12
+ recommendation: "Recommendation",
11
13
  more: (n) => `…and ${n} more`,
12
- passed: "Passed",
14
+ checklist: "Checklist",
15
+ legend: "✓ OK · ✖ error · ⚠ warning · ℹ note",
16
+ /** Findings of one rule in the checklist: «2 errors, 1 warning». */
17
+ count: (o) => [
18
+ o.errors && `${o.errors} ${o.errors === 1 ? "error" : "errors"}`,
19
+ o.warnings && `${o.warnings} ${o.warnings === 1 ? "warning" : "warnings"}`,
20
+ o.notes && `${o.notes} ${o.notes === 1 ? "note" : "notes"}`,
21
+ ]
22
+ .filter(Boolean)
23
+ .join(", "),
13
24
  site: "site",
14
25
  footer: (pages, date) => `Checked ${pages} pages on ${date}. Also check by hand after each deploy: the live CDN and bot protection with real AI agents (A-3), Google Search Console, Bing Webmaster Tools (ChatGPT search uses Bing), Yandex Webmaster.`,
15
26
  groups: {
@@ -15,8 +15,15 @@ export declare const LANGS: {
15
15
  }) => string;
16
16
  clean: string;
17
17
  fix: string;
18
+ recommendation: string;
18
19
  more: (n: number) => string;
19
- passed: string;
20
+ checklist: string;
21
+ legend: string;
22
+ count: (o: {
23
+ errors: number;
24
+ warnings: number;
25
+ notes: number;
26
+ }) => string;
20
27
  site: string;
21
28
  footer: (pages: number, date: string) => string;
22
29
  groups: {
@@ -166,8 +173,15 @@ export declare const LANGS: {
166
173
  }) => string;
167
174
  clean: string;
168
175
  fix: string;
176
+ recommendation: string;
169
177
  more: (n: number) => string;
170
- passed: string;
178
+ checklist: string;
179
+ legend: string;
180
+ count: (o: {
181
+ errors: number;
182
+ warnings: number;
183
+ notes: number;
184
+ }) => string;
171
185
  site: string;
172
186
  footer: (pages: number, date: string) => string;
173
187
  groups: {
package/dist/i18n/ru.js CHANGED
@@ -5,8 +5,11 @@ export const ru = {
5
5
  summary: (o) => `**${o.score}/100 (${o.grade})** · ошибок: ${o.errors} · предупреждений: ${o.warnings} · заметок: ${o.notes} · страниц: ${o.pages}`,
6
6
  clean: "Исправлять нечего: AI-поиск может прочитать, понять и процитировать эти страницы.",
7
7
  fix: "Как исправить",
8
+ recommendation: "Рекомендация",
8
9
  more: (n) => `…и ещё ${n}`,
9
- passed: "Пройдено",
10
+ checklist: "Чек-лист",
11
+ legend: "✓ в порядке · ✖ ошибка · ⚠ предупреждение · ℹ заметка",
12
+ count: (o) => [o.errors && `ошибок: ${o.errors}`, o.warnings && `предупреждений: ${o.warnings}`, o.notes && `заметок: ${o.notes}`].filter(Boolean).join(", "),
10
13
  site: "сайт",
11
14
  footer: (pages, date) => `Проверено страниц: ${pages}, ${date}. После каждого деплоя проверьте вручную: боевой CDN и защиту от ботов с настоящими AI-агентами (A-3), Google Search Console, Bing Webmaster Tools (поиск ChatGPT опирается на Bing), Яндекс Вебмастер.`,
12
15
  groups: {
package/dist/report.js CHANGED
@@ -27,15 +27,21 @@ export function toMarkdown(r) {
27
27
  lines.push(`- ${f.level === worst ? "" : `${ICON[f.level]} `}\`${short(f.where)}\` — ${f.message}`);
28
28
  if (findings.length > MAX_PER_RULE)
29
29
  lines.push(`- ${t.more(findings.length - MAX_PER_RULE)}`);
30
- lines.push("", `**${t.fix}:** ${rule.fix}`, "");
30
+ lines.push("", `**${worst === "info" ? t.recommendation : t.fix}:** ${rule.fix}`, "");
31
31
  }
32
- const passed = r.rules.filter((rule) => !byRule.has(rule.id));
33
- if (passed.length) {
34
- lines.push(`## ✓ ${t.passed}`, "");
35
- for (const g of Object.keys(t.groups)) {
36
- const list = passed.filter((p) => p.group === g);
37
- if (list.length)
38
- lines.push(`- **${t.groups[g]}:** ${list.map((p) => `${p.id} ${p.title}`).join("; ")}`);
32
+ // Every audited check by group, as in the README: what is fine and what is not, at a glance.
33
+ lines.push(`## ${t.checklist}`, "", t.legend, "");
34
+ for (const g of Object.keys(t.groups)) {
35
+ const list = r.rules.filter((rule) => rule.group === g).sort((a, b) => a.id.localeCompare(b.id, "en", { numeric: true }));
36
+ if (!list.length)
37
+ continue;
38
+ lines.push(`**${g}. ${t.groups[g]}**`, "");
39
+ for (const rule of list) {
40
+ const found = byRule.get(rule.id) ?? [];
41
+ const n = (level) => found.filter((f) => f.level === level).length;
42
+ const icon = found.length ? ICON[found.reduce((w, f) => (ORDER[f.level] < ORDER[w] ? f.level : w), "info")] : "✓";
43
+ const count = found.length ? ` — ${t.count({ errors: n("error"), warnings: n("warn"), notes: n("info") })}` : "";
44
+ lines.push(`- ${icon} ${rule.id} ${rule.title}${count}`);
39
45
  }
40
46
  lines.push("");
41
47
  }
package/package.json CHANGED
@@ -1,18 +1,47 @@
1
1
  {
2
2
  "name": "ai-search-auditor",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Checks whether AI search (ChatGPT, Claude, Perplexity, Google AI Overviews) can read, understand and cite your site: robots.txt for AI agents, text without JavaScript, JSON-LD authorship, llms.txt, freshness.",
5
- "keywords": ["ai search audit", "ai seo", "aeo", "geo", "generative engine optimization", "ai search", "llms.txt", "seo", "audit", "json-ld", "chatgpt", "claude", "perplexity"],
5
+ "keywords": [
6
+ "ai search audit",
7
+ "ai seo",
8
+ "aeo",
9
+ "geo",
10
+ "generative engine optimization",
11
+ "ai search",
12
+ "llms.txt",
13
+ "seo",
14
+ "audit",
15
+ "json-ld",
16
+ "chatgpt",
17
+ "claude",
18
+ "perplexity"
19
+ ],
6
20
  "license": "MIT",
7
21
  "author": "Aleksandr Bibikov (https://albibikov.com)",
8
22
  "homepage": "https://github.com/a-bibikov/ai-search-audit#readme",
9
- "repository": { "type": "git", "url": "git+https://github.com/a-bibikov/ai-search-audit.git" },
23
+ "repository": {
24
+ "type": "git",
25
+ "url": "git+https://github.com/a-bibikov/ai-search-audit.git"
26
+ },
10
27
  "bugs": "https://github.com/a-bibikov/ai-search-audit/issues",
11
28
  "type": "module",
12
- "bin": { "ai-search-auditor": "dist/cli.js", "ai-search-audit": "dist/cli.js" },
13
- "exports": { ".": "./dist/index.js" },
14
- "files": ["dist", "README.md", "LICENSE", "CHANGELOG.md"],
15
- "engines": { "node": ">=22.18" },
29
+ "bin": {
30
+ "ai-search-auditor": "dist/cli.js",
31
+ "ai-search-audit": "dist/cli.js"
32
+ },
33
+ "exports": {
34
+ ".": "./dist/index.js"
35
+ },
36
+ "files": [
37
+ "dist",
38
+ "README.md",
39
+ "LICENSE",
40
+ "CHANGELOG.md"
41
+ ],
42
+ "engines": {
43
+ "node": ">=22.18"
44
+ },
16
45
  "scripts": {
17
46
  "audit": "node src/cli.ts",
18
47
  "test": "node --test test/*.test.ts",