ax-audit 3.1.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (208) hide show
  1. package/CHANGELOG.md +198 -0
  2. package/LICENSE +1 -1
  3. package/README.md +89 -231
  4. package/dist/baseline.d.ts +2 -0
  5. package/dist/baseline.d.ts.map +1 -1
  6. package/dist/baseline.js +42 -4
  7. package/dist/baseline.js.map +1 -1
  8. package/dist/check-ids.d.ts +19 -0
  9. package/dist/check-ids.d.ts.map +1 -0
  10. package/dist/check-ids.js +53 -0
  11. package/dist/check-ids.js.map +1 -0
  12. package/dist/checks/agent-access.d.ts +33 -0
  13. package/dist/checks/agent-access.d.ts.map +1 -0
  14. package/dist/checks/agent-access.js +256 -0
  15. package/dist/checks/agent-access.js.map +1 -0
  16. package/dist/checks/agent-card.d.ts +37 -0
  17. package/dist/checks/agent-card.d.ts.map +1 -0
  18. package/dist/checks/agent-card.js +352 -0
  19. package/dist/checks/agent-card.js.map +1 -0
  20. package/dist/checks/agent-operability.d.ts +66 -0
  21. package/dist/checks/agent-operability.d.ts.map +1 -0
  22. package/dist/checks/agent-operability.js +383 -0
  23. package/dist/checks/agent-operability.js.map +1 -0
  24. package/dist/checks/agent-skills.d.ts +24 -0
  25. package/dist/checks/agent-skills.d.ts.map +1 -0
  26. package/dist/checks/agent-skills.js +316 -0
  27. package/dist/checks/agent-skills.js.map +1 -0
  28. package/dist/checks/ai-catalog.d.ts +28 -0
  29. package/dist/checks/ai-catalog.d.ts.map +1 -0
  30. package/dist/checks/ai-catalog.js +254 -0
  31. package/dist/checks/ai-catalog.js.map +1 -0
  32. package/dist/checks/ai-directives.d.ts +57 -0
  33. package/dist/checks/ai-directives.d.ts.map +1 -0
  34. package/dist/checks/ai-directives.js +263 -0
  35. package/dist/checks/ai-directives.js.map +1 -0
  36. package/dist/checks/api-discovery.d.ts +26 -0
  37. package/dist/checks/api-discovery.d.ts.map +1 -0
  38. package/dist/checks/api-discovery.js +432 -0
  39. package/dist/checks/api-discovery.js.map +1 -0
  40. package/dist/checks/auth-discovery.d.ts +28 -0
  41. package/dist/checks/auth-discovery.d.ts.map +1 -0
  42. package/dist/checks/auth-discovery.js +213 -0
  43. package/dist/checks/auth-discovery.js.map +1 -0
  44. package/dist/checks/commerce-discovery.d.ts +40 -0
  45. package/dist/checks/commerce-discovery.d.ts.map +1 -0
  46. package/dist/checks/commerce-discovery.js +295 -0
  47. package/dist/checks/commerce-discovery.js.map +1 -0
  48. package/dist/checks/content-negotiation.d.ts.map +1 -1
  49. package/dist/checks/content-negotiation.js +135 -20
  50. package/dist/checks/content-negotiation.js.map +1 -1
  51. package/dist/checks/crawl-efficiency.d.ts +16 -0
  52. package/dist/checks/crawl-efficiency.d.ts.map +1 -0
  53. package/dist/checks/crawl-efficiency.js +186 -0
  54. package/dist/checks/crawl-efficiency.js.map +1 -0
  55. package/dist/checks/frontmatter.d.ts +34 -0
  56. package/dist/checks/frontmatter.d.ts.map +1 -0
  57. package/dist/checks/frontmatter.js +100 -0
  58. package/dist/checks/frontmatter.js.map +1 -0
  59. package/dist/checks/html-rendering.d.ts.map +1 -1
  60. package/dist/checks/html-rendering.js +0 -1
  61. package/dist/checks/html-rendering.js.map +1 -1
  62. package/dist/checks/html-utils.d.ts +10 -0
  63. package/dist/checks/html-utils.d.ts.map +1 -1
  64. package/dist/checks/html-utils.js +19 -0
  65. package/dist/checks/html-utils.js.map +1 -1
  66. package/dist/checks/http-headers.d.ts.map +1 -1
  67. package/dist/checks/http-headers.js +82 -10
  68. package/dist/checks/http-headers.js.map +1 -1
  69. package/dist/checks/http-hygiene.d.ts +26 -0
  70. package/dist/checks/http-hygiene.d.ts.map +1 -0
  71. package/dist/checks/http-hygiene.js +257 -0
  72. package/dist/checks/http-hygiene.js.map +1 -0
  73. package/dist/checks/index.d.ts.map +1 -1
  74. package/dist/checks/index.js +30 -8
  75. package/dist/checks/index.js.map +1 -1
  76. package/dist/checks/llms-txt.d.ts +15 -0
  77. package/dist/checks/llms-txt.d.ts.map +1 -1
  78. package/dist/checks/llms-txt.js +162 -2
  79. package/dist/checks/llms-txt.js.map +1 -1
  80. package/dist/checks/mcp-discovery.d.ts +30 -0
  81. package/dist/checks/mcp-discovery.d.ts.map +1 -0
  82. package/dist/checks/mcp-discovery.js +523 -0
  83. package/dist/checks/mcp-discovery.js.map +1 -0
  84. package/dist/checks/meta-tags.d.ts.map +1 -1
  85. package/dist/checks/meta-tags.js +6 -5
  86. package/dist/checks/meta-tags.js.map +1 -1
  87. package/dist/checks/robots-parser.d.ts +110 -0
  88. package/dist/checks/robots-parser.d.ts.map +1 -0
  89. package/dist/checks/robots-parser.js +277 -0
  90. package/dist/checks/robots-parser.js.map +1 -0
  91. package/dist/checks/robots-txt.d.ts +2 -0
  92. package/dist/checks/robots-txt.d.ts.map +1 -1
  93. package/dist/checks/robots-txt.js +252 -45
  94. package/dist/checks/robots-txt.js.map +1 -1
  95. package/dist/checks/{mcp.d.ts → rsl.d.ts} +1 -1
  96. package/dist/checks/rsl.d.ts.map +1 -0
  97. package/dist/checks/rsl.js +242 -0
  98. package/dist/checks/rsl.js.map +1 -0
  99. package/dist/checks/security-txt.d.ts.map +1 -1
  100. package/dist/checks/security-txt.js +0 -1
  101. package/dist/checks/security-txt.js.map +1 -1
  102. package/dist/checks/seo-basics.d.ts.map +1 -1
  103. package/dist/checks/seo-basics.js +0 -1
  104. package/dist/checks/seo-basics.js.map +1 -1
  105. package/dist/checks/sitemap.d.ts.map +1 -1
  106. package/dist/checks/sitemap.js +0 -1
  107. package/dist/checks/sitemap.js.map +1 -1
  108. package/dist/checks/structured-data.d.ts.map +1 -1
  109. package/dist/checks/structured-data.js +215 -4
  110. package/dist/checks/structured-data.js.map +1 -1
  111. package/dist/checks/structured-fields.d.ts +46 -0
  112. package/dist/checks/structured-fields.d.ts.map +1 -0
  113. package/dist/checks/structured-fields.js +112 -0
  114. package/dist/checks/structured-fields.js.map +1 -0
  115. package/dist/checks/surface.d.ts +59 -0
  116. package/dist/checks/surface.d.ts.map +1 -0
  117. package/dist/checks/surface.js +106 -0
  118. package/dist/checks/surface.js.map +1 -0
  119. package/dist/checks/tls-https.d.ts.map +1 -1
  120. package/dist/checks/tls-https.js +0 -1
  121. package/dist/checks/tls-https.js.map +1 -1
  122. package/dist/checks/usage-policy.d.ts +53 -0
  123. package/dist/checks/usage-policy.d.ts.map +1 -0
  124. package/dist/checks/usage-policy.js +339 -0
  125. package/dist/checks/usage-policy.js.map +1 -0
  126. package/dist/checks/utils.d.ts +25 -1
  127. package/dist/checks/utils.d.ts.map +1 -1
  128. package/dist/checks/utils.js +33 -1
  129. package/dist/checks/utils.js.map +1 -1
  130. package/dist/checks/waf.d.ts +75 -0
  131. package/dist/checks/waf.d.ts.map +1 -0
  132. package/dist/checks/waf.js +203 -0
  133. package/dist/checks/waf.js.map +1 -0
  134. package/dist/checks/webmcp.d.ts +55 -0
  135. package/dist/checks/webmcp.d.ts.map +1 -0
  136. package/dist/checks/webmcp.js +209 -0
  137. package/dist/checks/webmcp.js.map +1 -0
  138. package/dist/checks/well-known.d.ts +38 -0
  139. package/dist/checks/well-known.d.ts.map +1 -0
  140. package/dist/checks/well-known.js +202 -0
  141. package/dist/checks/well-known.js.map +1 -0
  142. package/dist/cli.d.ts +14 -0
  143. package/dist/cli.d.ts.map +1 -1
  144. package/dist/cli.js +144 -6
  145. package/dist/cli.js.map +1 -1
  146. package/dist/constants.d.ts +212 -14
  147. package/dist/constants.d.ts.map +1 -1
  148. package/dist/constants.js +625 -59
  149. package/dist/constants.js.map +1 -1
  150. package/dist/fetcher.d.ts +5 -1
  151. package/dist/fetcher.d.ts.map +1 -1
  152. package/dist/fetcher.js +62 -27
  153. package/dist/fetcher.js.map +1 -1
  154. package/dist/guide-urls.js +1 -1
  155. package/dist/guide-urls.js.map +1 -1
  156. package/dist/index.d.ts +3 -1
  157. package/dist/index.d.ts.map +1 -1
  158. package/dist/index.js +2 -0
  159. package/dist/index.js.map +1 -1
  160. package/dist/orchestrator.d.ts +2 -2
  161. package/dist/orchestrator.d.ts.map +1 -1
  162. package/dist/orchestrator.js +18 -7
  163. package/dist/orchestrator.js.map +1 -1
  164. package/dist/reporter/html.d.ts +9 -0
  165. package/dist/reporter/html.d.ts.map +1 -1
  166. package/dist/reporter/html.js +49 -11
  167. package/dist/reporter/html.js.map +1 -1
  168. package/dist/reporter/index.d.ts.map +1 -1
  169. package/dist/reporter/index.js +7 -0
  170. package/dist/reporter/index.js.map +1 -1
  171. package/dist/reporter/markdown.d.ts +8 -0
  172. package/dist/reporter/markdown.d.ts.map +1 -0
  173. package/dist/reporter/markdown.js +106 -0
  174. package/dist/reporter/markdown.js.map +1 -0
  175. package/dist/reporter/terminal.d.ts.map +1 -1
  176. package/dist/reporter/terminal.js +36 -1
  177. package/dist/reporter/terminal.js.map +1 -1
  178. package/dist/scorer.d.ts +10 -0
  179. package/dist/scorer.d.ts.map +1 -1
  180. package/dist/scorer.js +22 -5
  181. package/dist/scorer.js.map +1 -1
  182. package/dist/types.d.ts +96 -3
  183. package/dist/types.d.ts.map +1 -1
  184. package/docs/api.md +200 -0
  185. package/docs/architecture.md +104 -0
  186. package/docs/checks.md +555 -0
  187. package/docs/ci.md +89 -0
  188. package/docs/cli.md +103 -0
  189. package/docs/concepts.md +101 -0
  190. package/docs/faq.md +89 -0
  191. package/docs/getting-started.md +108 -0
  192. package/docs/roadmap.md +367 -0
  193. package/package.json +14 -5
  194. package/dist/checks/agent-json.d.ts +0 -14
  195. package/dist/checks/agent-json.d.ts.map +0 -1
  196. package/dist/checks/agent-json.js +0 -167
  197. package/dist/checks/agent-json.js.map +0 -1
  198. package/dist/checks/mcp.d.ts.map +0 -1
  199. package/dist/checks/mcp.js +0 -162
  200. package/dist/checks/mcp.js.map +0 -1
  201. package/dist/checks/openapi.d.ts +0 -4
  202. package/dist/checks/openapi.d.ts.map +0 -1
  203. package/dist/checks/openapi.js +0 -121
  204. package/dist/checks/openapi.js.map +0 -1
  205. package/dist/checks/well-known-ai.d.ts +0 -17
  206. package/dist/checks/well-known-ai.d.ts.map +0 -1
  207. package/dist/checks/well-known-ai.js +0 -123
  208. package/dist/checks/well-known-ai.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -2,6 +2,204 @@
2
2
 
3
3
  All notable changes to ax-audit are documented here.
4
4
 
5
+ ## [4.0.0] - 2026-09-04
6
+
7
+ **Scores change on every site.** This release redistributes the weights, makes protocol checks conditional, and retires a check whose premise did not survive verification. Baselines written by earlier versions are read normally, but regression gating is suspended across the change — a rescore is not something the site did.
8
+
9
+ ### Breaking
10
+
11
+ - **Weights redistributed by what actually stops an agent.** Content leads at 33, because the failure that breaks the most agents is a page with nothing in its HTML: most crawlers do not run JavaScript, so content that appears only after hydration is invisible no matter how many discovery files a site publishes. `html-rendering` is the highest-weighted check at 11. Access is second at 24, because a firewall rule or a `nosnippet` directive silently undoes everything else. Discovery drops to 21, with llms.txt demoted from 11 to 5 on the evidence that most published files are never fetched by an AI search crawler.
12
+ - **Protocol checks are conditional.** A blog has no API to describe and is not an agent; scoring it zero said something false and made the overall number unreadable. Each protocol check now detects whether the site has the corresponding surface, reports **n/a** when it does not, and leaves the denominator. When the surface *is* present, a missing description is a stronger finding than before, and the wording says so: not "no MCP server discovery found" but "MCP server present but not discoverable".
13
+ - **`well-known-ai` retired.** Three of its five scored files had no consumer: `nlweb.json` appears in no NLWeb release, `genai.txt` has no specification, and `ai-plugin.json` described a product shut down in 2024. Its useful probes moved into the checks that own them in 3.8.
14
+ - **The 3.x scoring freezes are gone.** `robots-txt` scores against all twelve current core crawlers rather than the frozen eight, so the configuration that scored 100 in 3.6 now scores 90. Blocking a crawler deducts by purpose: 5 for a search crawler, whose absence removes the site from that assistant's answers, and 2 for a training crawler, where blocking is a defensible policy choice.
15
+ - **`CheckMeta.weight` is now optional** and nothing uses it. Weights live in `CHECK_WEIGHTS` alone. Having them in two places is what let them drift.
16
+
17
+ ### Added
18
+
19
+ - **`--profile api|mcp|agent|docs|commerce|all`** forces conditional checks applicable, for auditing against what a site intends to build rather than what it already has.
20
+ - **`--category content|discovery|access|policy|protocols`** runs one area. It narrows an explicit `--checks` selection rather than replacing it.
21
+ - **`--fail-on-category access:70,content:80`** gates CI per area. An overall score hides an area that is entirely broken: a site can score 80 while every access check fails because the other four carry it. Areas with nothing applicable are reported as not evaluated rather than failed.
22
+ - **Versioned baselines.** `schemaVersion` records which scoring model produced a baseline; a mismatch shows the deltas but suspends gating and says how to resume it. Baselines also record which checks were N/A, so gaining or losing a surface is not read as a regression or an improvement.
23
+
24
+ ### Migration
25
+
26
+ 1. Expect a different score. Sites with no API or MCP surface generally rise, because they are no longer marked down for lacking things they do not have. A bare page rose from 20 to 50 in testing; a documentation site with real infrastructure rose from 64 to 77.
27
+ 2. Re-save your baseline: `ax-audit <url> --save-baseline .ax-baseline.json`. Until you do, `--fail-on-regression` reports but does not gate.
28
+ 3. `--checks agent-json`, `--checks mcp` and `--checks openapi` still work; the ids were renamed in 3.7 and the old names remain aliases.
29
+ 4. If you relied on `well-known-ai`, its live probes are now in `usage-policy` (TDMRep) and the checks that own each file.
30
+
31
+ ### Fixed
32
+
33
+ - **JSON-LD was missed when `type` was not the first attribute** on the script tag. Next.js and several other frameworks emit `<script id="…" type="application/ld+json">`, so a large class of well-marked-up sites scored 0 on a 6-weight check and were told to add markup they already had. Three copies of the pattern now share one order-independent extractor.
34
+
35
+ ### Tests
36
+
37
+ 873 total. New coverage for surface detection, the weight distribution (sums to 100, no check declares its own, content outweighs every area), the CLI end to end via a subprocess, and baseline versioning in both directions.
38
+
39
+ ## [3.9.0] - 2026-09-04
40
+
41
+ Depth rather than breadth. One new check, and the existing readability checks extended to cover what agents actually do with a page once they reach it. Everything added is informational; scores are unchanged.
42
+
43
+ ### Added
44
+
45
+ - **agent-operability**: whether a browser agent can work the page or only look at it. Every major browser agent reads the accessibility tree, so a `<div onclick>` styled as a button does not appear at all — the agent does not see a button it cannot press, it sees nothing. Checks accessible names on buttons and links, labels on form controls, elements dressed as controls, dead links, table headers, iframe titles, `<time datetime>`, heading skips, unsized media, `<html lang>`, and entry-page obstacles. Reports proportions with a 90% threshold, and states on every run that it reads markup rather than a rendered tree.
46
+
47
+ ### Changed
48
+
49
+ - **llms.txt** gained the 2026 specification revision's additions — subpath scoping via `rel="describedby"`, per-page `.md` mirrors — plus link-health sampling. An llms.txt is written once and never revalidated while the site moves underneath it, so an index of dead links is its characteristic failure, and it wastes exactly the budget the file exists to save. Dead links are reported separately from redirecting ones. Every report now states who actually reads this file.
50
+ - **content-negotiation** stopped probing with a header no client sends. A bare `Accept: text/markdown` would pass against an implementation that fails every real request; the probe now sends what Claude Code, Cursor and OpenCode send. Added: origin-reported token counts preferred over a byte ratio, frontmatter validation (stripping the HTML also strips the canonical link, leaving an agent unable to attribute what it quotes), and probes for user-agent negotiation and `.md` suffix URLs before concluding a site has no Markdown.
51
+ - **structured-data** now reads provenance and freshness — `author`, `sameAs`, `publisher`, `dateModified` — and compares `headline` and `name` against the visible text, which is Google's one explicit requirement for structured data and AI features.
52
+ - **crawl-efficiency** reports a page's cost in tokens rather than bytes, naming the markup share an agent pays to receive and then discards, plus response time in agent terms.
53
+
54
+ ### Tests
55
+
56
+ 828 total, up from 777.
57
+
58
+ ## [3.8.0] - 2026-09-04
59
+
60
+ Eight new checks, all weight 0, plus the reporting machinery they need. Where 3.7 corrected what ax-audit was looking for, 3.8 covers the signals it was not looking at.
61
+
62
+ ### Added — reporting
63
+
64
+ - **Not-applicable checks.** A commerce-protocol check scoring zero on a personal blog says the blog is badly built; the honest answer is that the question does not arise. `CheckResult.applicable: false` excludes a check from both numerator and denominator, and reports render it as `n/a`. Everything counted against a site is now something the site could have done. A check whose meta exists but produced no result — because it crashed — still counts at full weight, so a broken check cannot inflate a score.
65
+ - **Category grouping.** Terminal, Markdown and HTML output group checks under content, discovery, access, policy and protocols. Twenty-six scores in a flat list do not tell a reader where the problem is.
66
+ - **`renderHtml` / `renderBatchHtml`**, returning the document as a string the way `renderMarkdown` already did. The HTML reporter could previously only print to stdout, which made it untestable and unusable from the programmatic API.
67
+
68
+ ### Added — checks
69
+
70
+ - **ai-directives**: the page-level controls Google and Microsoft document that they honor — `nosnippet` and `max-snippet` for AI Overviews and AI Mode, `noarchive` and `nocache` for Copilot grounding, `data-nosnippet` judged by what it wraps. The finding it exists for: a site that disallows `Google-Extended` expecting to leave AI Overviews has done no such thing, because that token governs Gemini training and grounding while AI Overviews follow Googlebot and the snippet directives.
71
+ - **usage-policy**: normalises Content Signals, AIPREF, RSL, TDMRep and `noai` onto three questions — train, ground, index — and reports where they disagree. A site whose robots.txt permits training while its RSL licence prohibits it has published two contradictory positions, and which one applies depends on which file a crawler read. Every report states that only robots.txt access rules are documented as honored by major operators.
72
+ - **http-hygiene**: status-code honesty. An agent cannot see that the 200 it received is a "page not found" screen; it stores the apology as the answer. Also redirect depth, `HEAD` support, `Retry-After` on 429, charset, and `<html lang>` against `Content-Language`.
73
+ - **ai-catalog**: the emerging single index of everything callable on a site, discovered the four ways Lighthouse's own audit accepts. Both specifications are drafts, so absence never scores — but a catalog whose entries point at documents that 404 does, because an agent trusts it before it fetches.
74
+ - **agent-skills**: installable procedures (SKILL.md) across all three competing discovery paths, validating the index and fetching a sample of the documents. Conditional on the site having a developer-facing surface.
75
+ - **webmcp**: forms declared as callable tools. Never asks for WebMCP, which is a Community Group draft in a Chrome origin trial; catches the mistakes static analysis can catch with certainty on pages that adopted it.
76
+ - **commerce-discovery**: the Universal Commerce Protocol profile, the one agentic-commerce specification with published site-side discovery. Validates the profile and resolves the schema URLs it promises. The alternatives define no manifest, and the report says so rather than inventing paths.
77
+ - **auth-discovery**: the RFC 9728 chain from `WWW-Authenticate` to the authorization server's metadata. A human hitting a 401 reads the documentation; an agent cannot.
78
+
79
+ ### Added — infrastructure
80
+
81
+ - `checks/frontmatter.ts`: a focused YAML frontmatter reader for SKILL.md and Markdown mirrors. ax-audit has two runtime dependencies and will not grow a YAML parser, so the reader handles the flat `key: value` block these documents use and records anything more elaborate as skipped rather than guessing.
82
+
83
+ ### Fixed
84
+
85
+ - **A lone `Offer` is a price, not a storefront.** Found by running commerce-discovery against a real SaaS site, which was told to build a commerce integration because it prices its plans with structured data. A bare `Offer` now counts only alongside a second signal such as a cart link.
86
+ - `buildResult` falls back to `CHECK_CATEGORIES`, so every result carries its category in JSON output too. Previously only checks setting `meta.category` explicitly did, leaving JSON consumers to reimplement the fallback the other reporters already had.
87
+
88
+ ### Scoring
89
+
90
+ Unchanged. All eight checks carry weight 0 and gain weight in 4.0.
91
+
92
+ ### Tests
93
+
94
+ 755 total, up from 553. New suites: ai-directives, usage-policy, http-hygiene, ai-catalog, agent-skills, webmcp, commerce-discovery, auth-discovery, frontmatter, and the N/A arithmetic including the crashed-check case.
95
+
96
+ ## [3.7.0] - 2026-09-04
97
+
98
+ A correction release. Three checks were probing paths that are no longer, or never were, the standard, and the crawler catalogue had drifted far enough to contain tokens that do not exist. Everything here was re-verified against vendor documentation, IANA, IETF datatracker and the relevant specification repositories on 2026-09-04.
99
+
100
+ **No score goes down.** Every correction that would have lowered an existing score is frozen behind a constant that is removed in 4.0, and tests assert it.
101
+
102
+ ### Fixed — wrong paths
103
+
104
+ - **A2A Agent Card moved.** The check probed `/.well-known/agent.json`. A2A relocated the card to `/.well-known/agent-card.json` in v0.3.0 (2025-07-30), and that path is IANA-registered. Both are probed, the registered one first; a card served only from the old path is validated and flagged. The check also handles both spec generations: A2A 1.0 (2026-03-12) folded `url`, `protocolVersion`, `preferredTransport` and `additionalInterfaces` into `supportedInterfaces[]`, and the generation is detected from the card's own structure rather than a version field. `authentication`, removed in 0.2.x in favour of `securitySchemes`, is now flagged. Renamed `agent-json` → `agent-card`.
105
+ - **`/.well-known/mcp.json` was never an MCP convention.** ax-audit recommended it before the ecosystem settled. Discovery now walks the real chain: `/.well-known/ai-catalog.json`, `/.well-known/mcp/server-card.json`, `<endpoint>/server-card`, then the legacy manifest. Server cards deliberately carry no `tools[]` — tool lists come from a live `tools/list` call. Protocol versions are checked against the five released revisions, with `2026-07-28` current. Renamed `mcp` → `mcp-discovery`.
106
+ - **`/.well-known/openapi.json` is a folk convention**, so a site publishing at `/openapi.json` scored zero. Discovery now runs in order of authority: RFC 9727's registered `/.well-known/api-catalog`, then RFC 8631 `service-desc` relations in headers and HTML, then ten conventional paths. YAML descriptions are recognised and reported as surface-validated only, since ax-audit ships no YAML parser. Renamed `openapi` → `api-discovery`.
107
+ - **`agent-access` was probing with `Google-Extended`.** That string is a robots.txt control token governing how an already-crawled page may be used; no request carries it, so the probe tested nothing. Token-only controls are excluded, leaving 10 real crawlers.
108
+ - **`http-headers` and `meta-tags` penalised correct behaviour**, requiring a discovery link to `agent.json` specifically. Either card path now counts.
109
+ - **An SPA catch-all page is absence, not corruption.** Found by running the new checks against a real site: probing `/mcp/server-card` returned HTTP 200 with the application's index shell, and the check reported a malformed server card on a site with no MCP server. `isHtmlDocument` now gates the speculative probes in `agent-card`, `mcp-discovery` and `api-discovery`.
110
+
111
+ ### Fixed — crawler catalogue
112
+
113
+ Verified against vendor documentation on 2026-09-04. Removed tokens that were never real user agents (`Gemini`, `GeminiBot`, `DeepSeek-AI`), whose products were discontinued (`NeevaBot`, `Operator`, `GoogleAgent-Mariner`), whose vendor operates no crawlers (Cohere), or that are not AI crawlers at all (the Awario social-listening family). Reclassified five user-triggered fetchers that were listed as training crawlers. Added `meta-webindexer`, `Amzn-SearchBot`, `Amzn-User`, `MistralAI-Index`, `MistralAI-Training`, `Google-GeminiNotebook`, `Applebot`, `ExaSearchBot`, `TikTokSpider`, `OAI-AdsBot`, `YandexAdditional`.
114
+
115
+ The catalogue is now organised by what a client does with a page — training, search, user-fetch, agent — because that determines the cost of blocking it. `CRAWLER_META` carries per token the vendor, whether robots.txt is honored, the published IP list, whether requests are signed with Web Bot Auth, and one sentence on what blocking costs. Findings quote it: "blocking OAI-SearchBot removes you from ChatGPT search answers", not "1 AI crawler blocked".
116
+
117
+ ### Added
118
+
119
+ - **Response classification.** `checks/waf.ts` distinguishes a deliberate block from a JavaScript challenge from anti-spoofing, because the remedies are completely different. Signatures verified 2026-09-04: `cf-mitigated: challenge`, `x-vercel-mitigated`, AWS WAF's 202 with `x-amzn-waf-action`, Cloudflare pay-per-crawl's `crawler-price`, x402's `payment-signature`, Web Bot Auth's `Accept-Signature`, RSL's `WWW-Authenticate: License`, plus thirteen body markers. Outcomes an unsigned probe cannot settle are scored at 0.75 and labelled inconclusive with the header observed — never as "blocks AI crawlers".
120
+ - **IETF AIPREF `Content-Usage:`** in robots.txt (draft-ietf-aipref-attach-05), with an optional path scope and a Structured Fields dictionary. The loudest finding is a vocabulary mix-up: AIPREF spells the training token `train-ai` with `y`/`n` while Content Signals and RSL spell it `ai-train` with `yes`/`no`, so `Content-Usage: ai-train=no` looks correct and does nothing.
121
+ - **Content Signals `use=immediate|reference|full`**, the fourth field Cloudflare added on 2026-07-01 and now serves from managed robots.txt. Cloudflare-managed blocks are detected and named.
122
+ - **Discovery link relations** in `http-headers`: `describedby` (llms.txt v2), `api-catalog` (RFC 9727), `service-desc` and `service-doc` (RFC 8631), `ai-catalog`, `c2pa-manifest`, `license`, markdown alternates, and the `X-Llms-Txt` header.
123
+ - **Content parity** in `agent-access` now fingerprints title, h1 and JSON-LD block count, catching cloaking that preserves word count.
124
+ - **Current well-known files** reported but never scored: the Web Bot Auth key directory, TDMRep, the OpenAI Apps verification token, `AGENTS.md`.
125
+ - **Fetcher transport options**: `method: 'HEAD'` and `redirect: 'manual'`, with `elapsedMs`, `redirected` and `redirectLocation` on every response.
126
+ - **Check categories** (content / discovery / access / policy / protocols) and `CheckResult.applicable` for N/A reporting, both consumed by reporters in 3.8.
127
+ - **`docs/roadmap.md`**: the research behind this release and the plan through 4.0.
128
+
129
+ ### Changed
130
+
131
+ - **robots.txt is parsed once.** `checks/robots-parser.ts` replaces three separate parsers that disagreed about what closes a User-agent group. Two parsing bugs fell out: `Disallow:` with an empty value now correctly means "allow all" per RFC 9309 §2.2.2 instead of being ignored, and a bot named in two groups is merged so a later `Disallow: /` is no longer hidden by an earlier `Allow: /`.
132
+ - **`well-known-ai` tells the truth.** Re-verification found three of its five scored files have no consumer: `/.well-known/nlweb.json` **does not exist** in any NLWeb release, document or commit (NLWeb exposes `/ask` and `/mcp`); `genai.txt` has no specification; `/ai-plugin.json` described ChatGPT plugins, shut down 2024-04-09. Omitting a retired format now reads as a pass with the evidence attached. The formula stays frozen for score stability; the check loses its weight and its retired probes in 4.0.
133
+ - **Renamed check ids keep working.** `CheckMeta.aliases` plus `src/check-ids.ts` resolve former ids in `--checks` selection and in baseline diffing, so a rename does not read as one check removed at a full regression and one added at zero.
134
+
135
+ ### Scoring
136
+
137
+ Unchanged. `robots-txt` deducts against the frozen eight-token core set and the token list 3.6 recognised; a site whose only MCP document is the legacy manifest is validated with the pre-3.7 rules reproduced exactly; `well-known-ai` keeps its five-file formula; every finding added inside a weighted check is informational. Broadened discovery can only raise a score, never lower one. Both freezes are removed in 4.0.
138
+
139
+ ### Tests
140
+
141
+ 553 total, up from 301. New suites: robots parser, structured fields, crawler catalogue, WAF classification, check-id aliasing, well-known registry, SPA-shell guard, fetcher transport. Two classes of test exist specifically to hold the no-regression promise: score-stability tests asserting a 3.6 perfect configuration still scores 100, and informational-finding tests asserting new findings leave scores untouched.
142
+
143
+ ## [3.6.0] - 2026-06-06
144
+
145
+ ### Added
146
+
147
+ - **Fetcher retries with exponential backoff**: transient failures (network errors, timeouts, and 408/425/429/500/502/503/504) are retried automatically. Configurable via `--retries <n>` (CLI, default 2) and `retries` (programmatic `AuditOptions`); backoff doubles from a 250ms base. Non-retryable responses (e.g. 404) short-circuit immediately. Previously a single transient timeout scored a check 0.
148
+ - **Parallel batch auditing**: `--concurrency <n>` (CLI) and `concurrency` on the new `BatchOptions` type run multiple URL audits in parallel via an order-preserving work queue. Default remains sequential (1).
149
+ - **Markdown reporter**: `--output markdown` emits a self-contained Markdown report (score, summary table, per-check findings with status emoji, baseline deltas) — ideal for CI logs and PR comments. Supported for single and batch audits. New exports: `renderMarkdown`, `renderBatchMarkdown`.
150
+ - **Crawler list refresh**: added Google's official signed AI-agent user-agent `Google-Agent` (identity `https://agent.bot.goog`) to the known-crawlers list.
151
+ - **CLI validation**: `--retries`, `--concurrency`, and `--output` now reject invalid values with a clear error.
152
+ - **17 new tests** (301 total): fetcher retry behavior (against a flaky local server), batch ordering/concurrency, and the Markdown reporter.
153
+
154
+ ### Notes
155
+
156
+ - No scoring changes. Retries can raise scores on flaky endpoints that previously timed out, but the scoring model itself is unchanged.
157
+
158
+ ## [3.5.0] - 2026-06-06
159
+
160
+ ### Added
161
+
162
+ - **crawl-efficiency check (informational)**: measures the cost of crawling your pages across three dimensions. Compression — rewards Brotli, accepts gzip/deflate/zstd (suggesting br), warns when uncompressed (−30). Conditional GET — checks for an `ETag` or `Last-Modified` validator, then issues a follow-up request with `If-None-Match` / `If-Modified-Since` and verifies the server returns `304 Not Modified` (−30 for no validator, −15 when 304 is not honored). Response size — warns on pages over 500 KB (−5) and 2 MB (−10) of decompressed HTML. The probe advertises `Accept-Encoding: br, gzip, deflate`; the conditional request reuses the per-request header support added in 3.1.0.
163
+ - **12 new tests** (284 total).
164
+
165
+ ### Scoring
166
+
167
+ - The new check carries **weight 0 in 3.x** (informational), consistent with 3.1.0–3.4.0.
168
+
169
+ ## [3.4.0] - 2026-06-06
170
+
171
+ ### Added
172
+
173
+ - **agent-access check (informational)**: cloaking and blocking detection. Probes the homepage with realistic user-agents for each of the 8 core AI crawlers (GPTBot, ClaudeBot, ChatGPT-User, Claude-SearchBot, Google-Extended, PerplexityBot, OAI-SearchBot, CCBot) and compares status and visible-text volume against the default-UA baseline. Flags the failure mode invisible to operators: robots.txt allows a crawler while the WAF returns 403 to its user-agent (Cloudflare's "Block AI Crawlers" toggle produces exactly this). Blocks consistent with an explicit robots.txt `Disallow` (or wildcard block) are reported as intentional and not penalized. Responses with under 50% of baseline visible text count as reduced content (half credit); content comparison is skipped for baselines under 200 chars to avoid SPA-shell noise. Hints note the verified-bots caveat: WAFs using Web Bot Auth / IP verification may pass the real crawler while rejecting this unverified probe.
174
+ - `parseUserAgents` and `BotEntry` are now exported from the robots-txt check for reuse.
175
+ - **12 new tests** (272 total).
176
+
177
+ ### Scoring
178
+
179
+ - Internal score is the credit ratio across the 8 probes; the check carries **weight 0 in 3.x** (informational), consistent with 3.1.0–3.3.0.
180
+
181
+ ## [3.3.0] - 2026-06-06
182
+
183
+ ### Added
184
+
185
+ - **rsl check (informational)**: validates [Really Simple Licensing 1.0](https://rslstandard.org/rsl) — the machine-readable content-licensing standard endorsed by 1,500+ publishers (Reddit, Yahoo, Medium, O'Reilly) with infrastructure support from Cloudflare and Fastly. Discovery via all three spec mechanisms: robots.txt `License:` directive (absolute-URI enforcement per §4.4.1), HTTP `Link: rel="license"; type="application/rsl+xml"` header, and `<link rel="license" type="application/rsl+xml">` (plain CC-style license links without the RSL media type are ignored). Document validation: `application/rsl+xml` Content-Type (−5), `<rsl>` root + `https://rslstandard.org/rsl` namespace, required `url` attribute on every `<content>` (empty value allowed per §3.3), `<license>` presence, `permits`/`prohibits` type and token vocabulary (`usage`: all/ai-all/ai-train/ai-input/ai-index/search; `user`; `geo` as ISO 3166-1 alpha-2), and `payment` types.
186
+ - **21 new tests** (260 total) covering the three discovery mechanisms, vocabulary enforcement, namespace/root/structure validation, XML-comment stripping, and score caps.
187
+
188
+ ### Scoring
189
+
190
+ - The new check carries **weight 0 in 3.x** (informational), consistent with 3.1.0/3.2.0: no impact on existing scores or baselines until v4.0.
191
+
192
+ ## [3.2.0] - 2026-06-06
193
+
194
+ ### Added
195
+
196
+ - **Content Signals Policy support in robots-txt** ([contentsignals.org](https://contentsignals.org), CC0): the check now parses `Content-Signal:` directives — the machine-readable `search` / `ai-input` / `ai-train` preferences that Cloudflare serves by default on its 3.8M+ managed robots.txt domains. Declared signals are reported per User-agent group; malformed segments, unknown signal names, and directives placed outside a User-agent group produce warnings. Absence of the directive produces an informational nudge. The group parser now also treats `Content-Signal` as a group-closing directive, fixing potential User-agent group leakage.
197
+ - **10 new tests** (239 total) covering declaration reporting, malformed/unknown signals, shared User-agent groups, case-insensitivity, out-of-group placement, and score neutrality.
198
+
199
+ ### Scoring
200
+
201
+ - All Content Signals findings are **informational in 3.x**: they never alter the robots-txt score, so existing scores and baselines are unchanged.
202
+
5
203
  ## [3.1.0] - 2026-06-06
6
204
 
7
205
  ### Added
package/LICENSE CHANGED
@@ -175,7 +175,7 @@
175
175
 
176
176
  END OF TERMS AND CONDITIONS
177
177
 
178
- Copyright 2026 Lucio Duran
178
+ Copyright 2026 Durani Technologies
179
179
 
180
180
  Licensed under the Apache License, Version 2.0 (the "License");
181
181
  you may not use this file except in compliance with the License.
package/README.md CHANGED
@@ -4,9 +4,9 @@
4
4
 
5
5
  <h1 align="center">ax-audit</h1>
6
6
 
7
- [![CI](https://github.com/lucioduran/ax-audit/actions/workflows/ci.yml/badge.svg)](https://github.com/lucioduran/ax-audit/actions/workflows/ci.yml)
7
+ [![CI](https://github.com/duranitech/ax-audit/actions/workflows/ci.yml/badge.svg)](https://github.com/duranitech/ax-audit/actions/workflows/ci.yml)
8
8
  [![npm version](https://img.shields.io/npm/v/ax-audit.svg)](https://www.npmjs.com/package/ax-audit)
9
- [![license](https://img.shields.io/npm/l/ax-audit.svg)](https://github.com/lucioduran/ax-audit/blob/main/LICENSE)
9
+ [![license](https://img.shields.io/npm/l/ax-audit.svg)](https://github.com/duranitech/ax-audit/blob/main/LICENSE)
10
10
  [![node](https://img.shields.io/node/v/ax-audit.svg)](https://nodejs.org)
11
11
 
12
12
  **Lighthouse for AI Agents.** Audit any website's AI Agent Experience (AX) readiness in seconds.
@@ -17,281 +17,139 @@ npx ax-audit https://your-site.com
17
17
 
18
18
  ```
19
19
  AX Audit Report
20
- https://lucioduran.com
21
-
22
- ███████████████████████████████████░░░░░ 88/100 Good
23
-
24
- LLMs.txt (100/100)
25
- PASS /llms.txt exists
26
- PASS /llms.txt Content-Type OK (text/plain)
27
- PASS H1 heading: "Lucio Duran — Personal Portfolio"
28
- PASS /llms-full.txt also available (bonus)
29
-
30
- Robots.txt (100/100)
31
- PASS All 8 core AI crawlers explicitly configured
32
- PASS 32/47 known AI crawlers have explicit rules
33
-
34
- HTML Rendering (90/100)
35
- PASS Server-rendered content detected (473 words)
36
- PASS Semantic landmarks present (main, article, header, footer, nav)
37
- PASS Single <h1> heading
38
- PASS 3/3 <img> tags have alt attributes
39
-
40
- TLS / HTTPS (100/100)
41
- PASS Site is served over HTTPS
42
- PASS HTTP requests redirect to HTTPS
43
- PASS HSTS preload-eligible
44
- ...
45
- ```
46
-
47
- ## Why
48
-
49
- AI agents and LLMs are increasingly crawling, indexing, and interacting with websites. Just like Lighthouse audits web performance and axe-core audits accessibility, **ax-audit** tells you how ready your site is for the AI agent ecosystem.
50
-
51
- ## What it checks
52
-
53
- | Check | What it audits | Weight |
54
- |---|---|---|
55
- | **LLMs.txt** | `/llms.txt` presence, [llmstxt.org](https://llmstxt.org) spec, Content-Type | 11% |
56
- | **Robots.txt** | AI crawler configuration (40+ known crawlers), wildcard detection, partial path restrictions | 11% |
57
- | **HTML Rendering** | Server-rendered content, semantic landmarks, SPA-shell detection, alt coverage | 9% |
58
- | **Structured Data** | JSON-LD on homepage (schema.org, `@graph`, entity types) | 9% |
59
- | **HTTP Headers** | Security headers + AI discovery `Link` headers + CORS on `.well-known` | 9% |
60
- | **Agent Card** | `/.well-known/agent.json` [A2A protocol](https://a2a-protocol.org) + same-origin url + skill quality | 7% |
61
- | **MCP** | `/.well-known/mcp.json` [Model Context Protocol](https://modelcontextprotocol.io) server config | 7% |
62
- | **SEO Basics** | `<title>`, meta description, canonical, `<html lang>`, charset, viewport, hreflang | 7% |
63
- | **Security.txt** | `/.well-known/security.txt` [RFC 9116](https://www.rfc-editor.org/rfc/rfc9116) compliance | 6% |
64
- | **Meta Tags** | AI meta tags (`ai:*`), `rel="alternate"`, `rel="me"`, OpenGraph + Twitter Card completeness | 6% |
65
- | **OpenAPI** | `/.well-known/openapi.json` presence, schema validity, Content-Type | 6% |
66
- | **TLS / HTTPS** | HTTPS, HTTP→HTTPS redirect, HSTS with `preload` + `includeSubDomains` | 5% |
67
- | **Sitemap** | `sitemap.xml` (or `Sitemap:` from robots.txt) — XML validity, `<lastmod>` coverage, freshness, sitemap-index handling | 4% |
68
- | **AI Well-Known** | Emerging files: `/.well-known/ai.txt`, `genai.txt`, `ai-plugin.json`, `agents.json`, `nlweb.json` | 3% |
69
- | **Content Negotiation** | Markdown for agents — `Accept: text/markdown` negotiation, `Vary: Accept`, `rel="alternate"` fallback | 0%* |
70
-
71
- \* **Content Negotiation** is informational in 3.x: it runs and reports findings but does not affect the overall score. It will gain weight in v4.0.
72
-
73
- ## Install
74
-
75
- ```bash
76
- npm install -g ax-audit
77
- ```
78
-
79
- Or run directly without installing:
80
-
81
- ```bash
82
- npx ax-audit https://your-site.com
83
- ```
84
-
85
- ## Usage
86
-
87
- ```bash
88
- # Full audit with colored terminal output
89
- ax-audit https://example.com
90
-
91
- # Batch audit — audit multiple URLs in a single run
92
- ax-audit https://example.com https://other-site.com https://third.dev
93
-
94
- # HTML report — self-contained, dark mode, shareable
95
- ax-audit https://example.com --output html > report.html
96
-
97
- # JSON output for CI/CD pipelines
98
- ax-audit https://example.com --json
99
-
100
- # Run only specific checks (validates IDs, errors on unknown)
101
- ax-audit https://example.com --checks llms-txt,robots-txt,agent-json
102
-
103
- # Custom timeout per request (default: 10s)
104
- ax-audit https://example.com --timeout 15000
105
-
106
- # Verbose mode — see every HTTP request, cache hit, and check score
107
- ax-audit https://example.com --verbose
108
-
109
- # Only show failures and warnings (hide passing findings)
110
- ax-audit https://example.com --only-failures
111
-
112
- # Save a baseline for future comparison
113
- ax-audit https://example.com --save-baseline baseline.json
114
-
115
- # Compare against a baseline — shows per-check score deltas
116
- ax-audit https://example.com --baseline baseline.json
20
+ https://example.com
117
21
 
118
- # Fail CI if any check regresses by more than 5 points
119
- ax-audit https://example.com --baseline baseline.json --fail-on-regression 5
120
- ```
22
+ █████████████████████████████████░░░░░░░ 83/100 Good
121
23
 
122
- ### Baseline Comparison
24
+ ── Content — is there substance an agent can read?
123
25
 
124
- Track score changes over time by saving a baseline and comparing against it in subsequent runs:
26
+ Structured Data (90/100)
27
+ PASS 3 JSON-LD block(s) found
28
+ WARN 1 structured-data value(s) do not appear in the visible text
29
+ 💡 Google's one explicit requirement for structured data and AI
30
+ features is that it match what a reader sees.
125
31
 
126
- ```bash
127
- # First run — save the baseline
128
- ax-audit https://example.com --save-baseline .ax-baseline.json
32
+ ── Access — can an agent actually retrieve it?
129
33
 
130
- # Later — compare against the baseline
131
- ax-audit https://example.com --baseline .ax-baseline.json
132
- ```
34
+ Agent Access (100/100)
35
+ PASS All 10 core AI crawler user-agents receive the same page as a regular client
133
36
 
134
- ```
135
- AX Audit Report
136
- https://example.com
137
- Baseline: 2026-04-15T12:00:00.000Z
37
+ AI Directives (100/100)
38
+ PASS Homepage is indexable
39
+ PASS No directive restricts how AI assistants may use this page
138
40
 
139
- ████████████████████████████████░░░░░░░░ 82/100 Good ▲7
41
+ ── Protocols — what can an agent call?
140
42
 
141
- LLMs.txt (100/100) ▲20
142
- Robots.txt (70/100) ▼10
43
+ MCP Discovery (n/a)
44
+ PASS No MCP server — MCP discovery does not apply to this site
143
45
  ...
144
-
145
- Regressions
146
- Robots.txt: 80 → 70 (▼10)
147
-
148
- Improvements
149
- LLMs.txt: 80 → 100 (▲20)
150
46
  ```
151
47
 
152
- Works with all output formats (terminal, JSON, HTML). In JSON mode, a `baselineDiff` object is included with per-check deltas.
48
+ ## Why
153
49
 
154
- Use `--fail-on-regression <points>` in CI to fail the build if any individual check drops by more than the specified threshold.
50
+ AI agents crawl, cite and act on websites. Lighthouse audits performance, axe-core audits accessibility, and **ax-audit** tells you how ready your site is for agents discovery files, crawler policy, usage rights, content negotiation, and the failures that are invisible from the inside:
155
51
 
156
- ### Batch Mode
52
+ - Your robots.txt allows GPTBot and your firewall returns 403 to it.
53
+ - You blocked `Google-Extended` expecting to leave AI Overviews. It does not do that.
54
+ - Your robots.txt permits AI training while your RSL licence prohibits it, so which terms apply depends on which file a crawler read.
55
+ - Your content only exists after hydration, so the crawlers that do not run JavaScript see an empty page.
56
+ - A missing page answers `200 OK`, so an agent stores the apology as the answer.
157
57
 
158
- Pass multiple URLs to audit them sequentially. Each gets its own full report, followed by a summary table:
58
+ ## What it checks
159
59
 
160
- ```
161
- ═══ Batch Summary ═══
60
+ 26 checks across five areas. Full reference: **[docs/checks.md](docs/checks.md)**.
162
61
 
163
- URL Score Grade
164
- ────────────────────────────────────────────────────────────
165
- https://example.com 92/100 Excellent
166
- https://other-site.com 45/100 Poor
62
+ | Area | Weight | Checks |
63
+ |---|---|---|
64
+ | **Content** — is there substance an agent can read? | 33% | HTML Rendering · Agent Operability · Structured Data · SEO Basics · Content Negotiation |
65
+ | **Access** — can an agent actually retrieve it? | 24% | Agent Access · AI Directives · HTTP Hygiene · TLS/HTTPS · Crawl Efficiency |
66
+ | **Discovery** — can an agent find your machine-readable files? | 21% | Robots.txt · LLMs.txt · HTTP Headers · Sitemap · Meta Tags |
67
+ | **Protocols** — what can an agent call? | 13% | API Discovery · Agent Card · MCP Discovery · Agent Skills · Auth Discovery |
68
+ | **Policy** — what usage rights do you declare? | 9% | Usage Policy · Security.txt · RSL License |
69
+ | Draft specifications, reported but never scored | 0% | AI Catalog · WebMCP · Commerce Discovery |
167
70
 
168
- 2 URLs audited: 1 passed, 1 failed
169
- ████████████████████████████░░░░░░░░░░░░ 69/100 avg Fair
170
- ```
71
+ Content leads because the failure that breaks the most agents is a page with nothing in its HTML. Most crawlers do not run JavaScript, so a site whose content appears only after hydration is invisible to them no matter how many discovery files it publishes.
171
72
 
172
- Exit code: `0` if all URLs score >= 70, `1` if any fails.
73
+ Protocol checks are **conditional**: a blog has no API to describe, so those report `n/a` and are excluded from the score rather than counted as failures. Use `--profile` to audit against what a site intends to build.
173
74
 
174
- ### HTML Report
75
+ Every finding links to a step-by-step **[remediation guide](https://axrush.com/guides)**.
175
76
 
176
- Generate a self-contained HTML report you can open in any browser or share with your team:
77
+ ## Usage
177
78
 
178
79
  ```bash
179
- ax-audit https://example.com --output html > report.html
80
+ ax-audit https://example.com # full audit, terminal output
81
+ ax-audit https://a.com https://b.com --concurrency 2 # batch, in parallel
82
+ ax-audit https://example.com --output markdown # also: json, html
83
+ ax-audit https://example.com --checks llms-txt,rsl # subset of checks
84
+ ax-audit https://example.com --only-failures # hide passing findings
85
+ ax-audit https://example.com --category access # one area only
86
+ ax-audit https://example.com --profile api # audit as though it had an API
87
+ ax-audit https://example.com --fail-on-category access:70 # per-area CI gate
88
+ ax-audit https://example.com --baseline .ax-baseline.json --fail-on-regression 5
180
89
  ```
181
90
 
182
- Features: circular score gauge, dark/light mode, collapsible check sections, responsive design. Works for both single and batch audits.
91
+ Exit codes gate CI: `0` for score ≥ 70, `1` below. Full flag reference: **[docs/cli.md](docs/cli.md)** · CI recipes (PR comments, regression gates, scheduled audits): **[docs/ci.md](docs/ci.md)**.
183
92
 
184
93
  ## Programmatic API
185
94
 
186
- Full TypeScript support with all types exported.
187
-
188
95
  ```typescript
189
96
  import { audit, batchAudit } from 'ax-audit';
190
- import type { AuditReport, BatchAuditReport } from 'ax-audit';
191
-
192
- // Single URL
193
- const report: AuditReport = await audit({ url: 'https://example.com' });
194
- console.log(report.overallScore); // 0-100
195
- console.log(report.grade.label); // 'Excellent' | 'Good' | 'Fair' | 'Poor'
196
- console.log(report.results); // Individual check results with findings
197
-
198
- // Batch audit
199
- const batch: BatchAuditReport = await batchAudit(
200
- ['https://example.com', 'https://other.com'],
201
- { timeout: 10000 }
202
- );
203
- console.log(batch.summary.averageScore); // Average across all URLs
204
- console.log(batch.summary.passed); // Number of URLs scoring >= 70
205
- ```
206
-
207
- Also exports `calculateOverallScore`, `getGrade`, `checks`, `saveBaseline`, `loadBaseline`, `diffBaseline`, and `toBaselineData` for advanced usage.
208
-
209
- ## Scoring
210
-
211
- Each check returns a score from 0 to 100. The overall score is a weighted average across all checks.
212
97
 
213
- | Grade | Score | Exit Code |
214
- |---|---|---|
215
- | Excellent | 90 - 100 | `0` |
216
- | Good | 70 - 89 | `0` |
217
- | Fair | 50 - 69 | `1` |
218
- | Poor | 0 - 49 | `1` |
219
-
220
- Exit codes make it easy to gate CI/CD deployments on AX readiness.
98
+ const report = await audit({ url: 'https://example.com' });
99
+ report.overallScore; // 0–100
100
+ report.results; // per-check findings
101
+ ```
221
102
 
222
- ## CI Integration
103
+ Full API and types: **[docs/api.md](docs/api.md)**.
223
104
 
224
- ### GitHub Actions
105
+ ## Documentation
225
106
 
226
- ```yaml
227
- - name: AX Audit
228
- run: npx ax-audit https://your-site.com
229
- # Fails the step if score < 70
230
- ```
107
+ Start here:
231
108
 
232
- Save the report as an artifact:
109
+ | Document | Contents |
110
+ |---|---|
111
+ | [docs/getting-started.md](docs/getting-started.md) | First audit, reading the report, fixing in impact order |
112
+ | [docs/concepts.md](docs/concepts.md) | The AX standards landscape — llms.txt, A2A, MCP, RSL, Content Signals, Web Bot Auth |
233
113
 
234
- ```yaml
235
- - name: AX Audit (JSON)
236
- run: npx ax-audit https://your-site.com --json > ax-report.json
114
+ Reference:
237
115
 
238
- - uses: actions/upload-artifact@v4
239
- with:
240
- name: ax-audit-report
241
- path: ax-report.json
242
- ```
116
+ | Document | Contents |
117
+ |---|---|
118
+ | [docs/checks.md](docs/checks.md) | All 26 checks with **exact scoring** per finding, the weight table, and the conditional-check rule |
119
+ | [docs/cli.md](docs/cli.md) | Every flag, profiles, area filters, per-area CI gates, baseline workflow |
120
+ | [docs/api.md](docs/api.md) | `audit`, `batchAudit`, baselines, reporters, types, API-stability policy |
121
+ | [docs/ci.md](docs/ci.md) | GitHub Actions recipes: gates, PR comments, scheduled drift detection |
122
+ | [docs/architecture.md](docs/architecture.md) | Pipeline design, check anatomy, how to add a check, scoring policy |
123
+ | [docs/faq.md](docs/faq.md) | Troubleshooting, false positives, the `agent-access` verified-bots caveat |
124
+ | [Remediation guides](https://axrush.com/guides) | Step-by-step fixes for every finding |
243
125
 
244
- Fail on regressions using a committed baseline:
126
+ The same documentation is browsable at [axrush.com/docs](https://axrush.com/docs), rendered from these files. Contributors: see [CONTRIBUTING.md](CONTRIBUTING.md) and [SECURITY.md](SECURITY.md).
245
127
 
246
- ```yaml
247
- - name: AX Audit (regression gate)
248
- run: npx ax-audit https://your-site.com --baseline .ax-baseline.json --fail-on-regression 5
249
- ```
250
-
251
- ## Available Checks
128
+ ## Scoring
252
129
 
253
- | Check ID | Use with `--checks` |
254
- |---|---|
255
- | `llms-txt` | LLMs.txt spec + Content-Type |
256
- | `robots-txt` | AI crawler configuration (40+ crawlers) |
257
- | `html-rendering` | SSR / SPA-shell detection + semantic HTML |
258
- | `structured-data` | JSON-LD structured data |
259
- | `http-headers` | Security + AI discovery headers |
260
- | `agent-json` | A2A Agent Card + same-origin validation |
261
- | `mcp` | MCP server configuration |
262
- | `seo-basics` | title / description / canonical / lang / hreflang |
263
- | `security-txt` | RFC 9116 Security.txt |
264
- | `meta-tags` | AI meta tags + OpenGraph + Twitter Card |
265
- | `openapi` | OpenAPI specification |
266
- | `tls-https` | HTTPS + HTTP→HTTPS redirect + HSTS preload |
267
- | `sitemap` | sitemap.xml validation + freshness |
268
- | `well-known-ai` | Emerging AI discovery files |
269
- | `content-negotiation` | Markdown via `Accept: text/markdown` (informational) |
270
-
271
- ## Testing
130
+ | Grade | Score | Exit Code |
131
+ |---|---|---|
132
+ | Excellent | 90–100 | `0` |
133
+ | Good | 70–89 | `0` |
134
+ | Fair | 50–69 | `1` |
135
+ | Poor | 0–49 | `1` |
272
136
 
273
- ```bash
274
- npm test
275
- ```
137
+ Checks that do not apply to a site report **n/a** and leave the denominator entirely, rather than scoring 0. A blog is not marked down for having no API to describe. Everything counted against a site is something the site could have done — which is what makes a low score worth acting on.
276
138
 
277
- 229 tests covering all 15 checks, the scorer, the HTTP fetcher (against a real local server), baseline comparison, HTML parsing utilities, and edge cases. Uses Node.js built-in test runner (`node:test`), no extra test dependencies.
139
+ `--fail-on-category access:70` gates CI per area, because an overall score can hide an area that is entirely broken while the other four carry it.
278
140
 
279
- ## Tech Stack
141
+ ## Tech
280
142
 
281
- - **TypeScript** with strict mode
282
- - **2 runtime dependencies**: `chalk` + `commander`
283
- - **Node.js 18+** built-in `fetch` (zero HTTP libraries)
284
- - Parallel check execution via `Promise.allSettled`
285
- - In-memory request caching per audit run
143
+ TypeScript strict mode · 2 runtime dependencies (`chalk`, `commander`) · Node 18+ built-in `fetch` · no HTML, XML or YAML parser dependencies · parallel checks via `Promise.allSettled` · per-run request cache with `Vary`-aware keys · transient-failure retries with backoff · 873 tests on `node:test` with zero test dependencies.
286
144
 
287
145
  ## Contributing
288
146
 
289
- Contributions are welcome. To add a new check:
147
+ Contributions are welcome — see **[docs/architecture.md](docs/architecture.md)** for the pipeline design, check anatomy, and the steps (code, tests, docs, remediation guide) a new check requires.
148
+
149
+ ## Related
290
150
 
291
- 1. Create `src/checks/your-check.ts` exporting `default` (async check function) and `meta` (CheckMeta)
292
- 2. Use `buildResult(meta, score, findings, start)` from `./utils.js` to return results
293
- 3. Register it in `src/checks/index.ts`
294
- 4. Add its weight to `CHECK_WEIGHTS` in `src/constants.ts`
151
+ - **[ax-init](https://github.com/duranitech/ax-init)** generate the AX files this tool audits
152
+ - **[ax-cite](https://github.com/duranitech/ax-cite)** embed AI-extractable structured data in your pages
295
153
 
296
154
  ## License
297
155
 
@@ -299,4 +157,4 @@ Contributions are welcome. To add a new check:
299
157
 
300
158
  ---
301
159
 
302
- Built by [Lucio Duran](https://lucioduran.com)
160
+ **[AX Rush](https://axrush.com)** — the agent-experience toolkit, by [Durani Technologies](https://github.com/duranitech).
@@ -3,6 +3,8 @@ import type { AuditReport, BaselineData, BaselineDiff } from './types.js';
3
3
  * Extract a minimal, stable snapshot from an AuditReport suitable for
4
4
  * persistence and future comparison.
5
5
  */
6
+ /** Scoring model this build produces. Bumped whenever weights or the scoring rules change. */
7
+ export declare const BASELINE_SCHEMA_VERSION = 2;
6
8
  export declare function toBaselineData(report: AuditReport): BaselineData;
7
9
  /**
8
10
  * Persist a baseline to disk as pretty-printed JSON.
@@ -1 +1 @@
1
- {"version":3,"file":"baseline.d.ts","sourceRoot":"","sources":["../src/baseline.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,EAAa,MAAM,YAAY,CAAC;AAErF;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,WAAW,GAAG,YAAY,CAWhE;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,IAAI,CAIpE;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,CAwBvD;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,YAAY,EAAE,MAAM,EAAE,WAAW,GAAG,YAAY,CAsCtF"}
1
+ {"version":3,"file":"baseline.d.ts","sourceRoot":"","sources":["../src/baseline.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,EAAa,MAAM,YAAY,CAAC;AAErF;;;GAGG;AACH,8FAA8F;AAC9F,eAAO,MAAM,uBAAuB,IAAI,CAAC;AAEzC,wBAAgB,cAAc,CAAC,MAAM,EAAE,WAAW,GAAG,YAAY,CAiBhE;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,IAAI,CAIpE;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,CAwBvD;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,YAAY,EAAE,MAAM,EAAE,WAAW,GAAG,YAAY,CAkEtF"}