aeo-ready 1.0.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +7 -0
- package/bin/cli.js +9 -62
- package/package.json +5 -4
- package/src/benchmark/cloudflare.js +75 -0
- package/src/benchmark/fern.js +51 -0
- package/src/benchmark/index.js +119 -0
- package/src/dashboard/generate.js +22 -48
- package/src/dashboard/sections/benchmark-details.js +79 -0
- package/src/dashboard/sections/history-table.js +10 -7
- package/src/dashboard/sections/overall-score.js +55 -118
- package/src/dashboard/sections/trend-chart.js +31 -46
- package/src/history/index.js +8 -11
- package/src/scan.js +58 -294
- package/.aeo-ready/dashboard.html +0 -339
- package/src/checks/agent-readiness/actionable.js +0 -165
- package/src/checks/agent-readiness/capability.js +0 -209
- package/src/checks/agent-readiness/content-structure.js +0 -242
- package/src/checks/agent-readiness/discovery.js +0 -231
- package/src/checks/ai-visibility/authority.js +0 -195
- package/src/checks/ai-visibility/citation-readiness.js +0 -228
- package/src/checks/ai-visibility/freshness.js +0 -182
- package/src/checks/ai-visibility/structured-data.js +0 -180
- package/src/dashboard/sections/agent-readiness.js +0 -71
- package/src/dashboard/sections/ai-visibility.js +0 -67
- package/src/dashboard/sections/recommendations.js +0 -49
- package/src/fix/generators/agents-json.js +0 -73
- package/src/fix/generators/agents-md.js +0 -85
- package/src/fix/generators/llms-txt.js +0 -166
- package/src/fix/generators/robots-txt.js +0 -64
- package/src/fix/index.js +0 -177
- package/src/track/index.js +0 -167
- package/src/utils/detect-type.js +0 -99
- package/src/utils/tokens.js +0 -18
|
@@ -1,339 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8">
|
|
5
|
-
<title>aeo-ready — AI Readiness Dashboard</title>
|
|
6
|
-
<style>
|
|
7
|
-
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
8
|
-
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; background: #0d1117; color: #c9d1d9; display: flex; }
|
|
9
|
-
nav { position: fixed; top: 0; left: 0; width: 200px; height: 100vh; background: #010409; border-right: 1px solid #21262d; padding: 0; overflow-y: auto; z-index: 10; }
|
|
10
|
-
nav h2 { color: #f0f6fc; font-size: 13px; letter-spacing: 0.02em; padding: 20px 16px 14px; border-bottom: 1px solid #21262d; margin: 0 0 8px; }
|
|
11
|
-
nav a { display: block; padding: 6px 16px; font-size: 12px; color: #8b949e; text-decoration: none; transition: color 0.15s; }
|
|
12
|
-
nav a:hover { color: #f0f6fc; }
|
|
13
|
-
nav a.section-head { color: #c9d1d9; font-weight: 600; font-size: 11px; text-transform: uppercase; letter-spacing: 0.04em; margin-top: 12px; }
|
|
14
|
-
main { margin-left: 200px; padding: 32px 40px; max-width: 900px; width: 100%; }
|
|
15
|
-
h1 { color: #f0f6fc; font-size: 24px; margin-bottom: 4px; }
|
|
16
|
-
.subtitle { color: #8b949e; font-size: 13px; margin-bottom: 32px; }
|
|
17
|
-
h2 { color: #f0f6fc; font-size: 18px; margin-top: 40px; margin-bottom: 16px; padding-top: 16px; border-top: 1px solid #21262d; }
|
|
18
|
-
h3 { color: #f0f6fc; font-size: 14px; margin-top: 16px; margin-bottom: 8px; }
|
|
19
|
-
.score-hero { text-align: center; padding: 32px 0; }
|
|
20
|
-
.score-hero .grade { font-size: 64px; font-weight: 700; }
|
|
21
|
-
.score-hero .number { font-size: 24px; color: #8b949e; margin-top: 4px; }
|
|
22
|
-
.score-hero .bar { margin: 16px auto; width: 300px; height: 8px; background: #21262d; border-radius: 4px; overflow: hidden; }
|
|
23
|
-
.score-hero .bar-fill { height: 100%; border-radius: 4px; }
|
|
24
|
-
.grade-a { color: #3fb950; } .grade-b { color: #79c0ff; } .grade-c { color: #d29922; } .grade-d { color: #f85149; } .grade-f { color: #f85149; }
|
|
25
|
-
.bar-a { background: #3fb950; } .bar-b { background: #79c0ff; } .bar-c { background: #d29922; } .bar-d { background: #f85149; } .bar-f { background: #f85149; }
|
|
26
|
-
.scorecard { background: #161b22; border: 1px solid #21262d; border-radius: 8px; padding: 20px; margin: 16px 0; }
|
|
27
|
-
.scorecard-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px; }
|
|
28
|
-
.scorecard-header h3 { margin: 0; }
|
|
29
|
-
.scorecard-header .pct { font-size: 20px; font-weight: 600; }
|
|
30
|
-
.category { margin: 12px 0; padding: 8px 0; border-bottom: 1px solid #21262d; }
|
|
31
|
-
.category:last-child { border-bottom: none; }
|
|
32
|
-
.cat-header { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
|
|
33
|
-
.cat-name { font-size: 13px; font-weight: 500; flex: 1; }
|
|
34
|
-
.cat-score { font-size: 12px; color: #8b949e; }
|
|
35
|
-
.cat-bar { width: 120px; height: 4px; background: #21262d; border-radius: 2px; overflow: hidden; }
|
|
36
|
-
.cat-bar-fill { height: 100%; border-radius: 2px; }
|
|
37
|
-
.check { font-size: 12px; padding: 2px 0 2px 16px; color: #8b949e; }
|
|
38
|
-
.check.pass { color: #3fb950; }
|
|
39
|
-
.check.fail { color: #f85149; }
|
|
40
|
-
.check .fix { display: block; color: #8b949e; font-size: 11px; padding-left: 16px; margin-top: 2px; }
|
|
41
|
-
table { width: 100%; border-collapse: collapse; margin: 12px 0; }
|
|
42
|
-
th, td { text-align: left; padding: 8px 12px; font-size: 12px; border-bottom: 1px solid #21262d; }
|
|
43
|
-
th { color: #8b949e; font-weight: 600; }
|
|
44
|
-
.trend { color: #3fb950; } .regression { color: #f85149; }
|
|
45
|
-
.rec { background: #161b22; border: 1px solid #21262d; border-radius: 6px; padding: 12px 16px; margin: 8px 0; }
|
|
46
|
-
.rec-title { font-size: 13px; font-weight: 500; color: #f0f6fc; }
|
|
47
|
-
.rec-fix { font-size: 12px; color: #8b949e; margin-top: 4px; }
|
|
48
|
-
.rec-impact { font-size: 11px; color: #d29922; margin-top: 4px; }
|
|
49
|
-
svg { display: block; margin: 16px 0; }
|
|
50
|
-
</style>
|
|
51
|
-
</head>
|
|
52
|
-
<body>
|
|
53
|
-
|
|
54
|
-
<nav>
|
|
55
|
-
<h2>aeo-ready</h2>
|
|
56
|
-
<a href="#overall" class="section-head">Overall Score</a>
|
|
57
|
-
<a href="#agent-readiness" class="section-head">Agent Readiness</a>
|
|
58
|
-
<a href="#ai-visibility" class="section-head">AI Visibility</a>
|
|
59
|
-
<a href="#trends" class="section-head">Trends</a>
|
|
60
|
-
<a href="#history" class="section-head">History</a>
|
|
61
|
-
<a href="#recommendations" class="section-head">Recommendations</a>
|
|
62
|
-
</nav>
|
|
63
|
-
|
|
64
|
-
<main>
|
|
65
|
-
|
|
66
|
-
<h1>AI Readiness Dashboard</h1>
|
|
67
|
-
<p class="subtitle">/Users/katlaszlo/Desktop/Github-Wiki/GitHub/agent-web · Updated 2026-05-18</p>
|
|
68
|
-
|
|
69
|
-
<!-- SECTION:overall-score -->
|
|
70
|
-
<div class="score-hero" id="overall">
|
|
71
|
-
<div class="grade grade-c">C</div>
|
|
72
|
-
<div class="number">50 / 100</div>
|
|
73
|
-
<div class="bar"><div class="bar-fill bar-c" style="width:50%"></div></div>
|
|
74
|
-
<div style="display:flex;justify-content:center;gap:40px;margin-top:16px;font-size:13px;">
|
|
75
|
-
<span>Agent Readiness: <strong>22/50</strong></span>
|
|
76
|
-
<span>AI Visibility: <strong>28/50</strong></span>
|
|
77
|
-
</div>
|
|
78
|
-
</div>
|
|
79
|
-
<div style="text-align:center;padding:12px 24px;margin:16px auto;max-width:500px;background:#d2992210;border:1px solid #d2992233;border-radius:6px;font-size:13px;color:#d29922;">
|
|
80
|
-
<strong>Agent Readiness is low.</strong> AI engines can't cite what they can't read.<br>
|
|
81
|
-
<span style="color:#8b949e;">Fix the agent side first. Visibility follows.</span>
|
|
82
|
-
</div>
|
|
83
|
-
<div style="margin-top:24px;padding:20px;background:#161b22;border:1px solid #21262d;border-radius:8px;">
|
|
84
|
-
<div style="font-size:13px;font-weight:600;color:#f0f6fc;margin-bottom:12px;">How you compare</div>
|
|
85
|
-
<div style="display:flex;align-items:center;gap:8px;margin:6px 0;">
|
|
86
|
-
<span style="width:120px;font-size:12px;color:#8b949e;">Cloudflare</span>
|
|
87
|
-
<div style="flex:1;height:4px;background:#21262d;border-radius:2px;overflow:hidden;">
|
|
88
|
-
<div style="width:55%;height:100%;background:#30363d;border-radius:2px;"></div>
|
|
89
|
-
</div>
|
|
90
|
-
<span style="font-size:11px;color:#8b949e;width:32px;text-align:right;">55</span>
|
|
91
|
-
</div>
|
|
92
|
-
<div style="display:flex;align-items:center;gap:8px;margin:6px 0;">
|
|
93
|
-
<span style="width:120px;font-size:12px;color:#8b949e;">Supabase</span>
|
|
94
|
-
<div style="flex:1;height:4px;background:#21262d;border-radius:2px;overflow:hidden;">
|
|
95
|
-
<div style="width:52%;height:100%;background:#30363d;border-radius:2px;"></div>
|
|
96
|
-
</div>
|
|
97
|
-
<span style="font-size:11px;color:#8b949e;width:32px;text-align:right;">52</span>
|
|
98
|
-
</div>
|
|
99
|
-
<div style="display:flex;align-items:center;gap:8px;margin:6px 0;">
|
|
100
|
-
<span style="width:120px;font-size:12px;color:#f0f6fc;font-weight:600;">You</span>
|
|
101
|
-
<div style="flex:1;height:4px;background:#21262d;border-radius:2px;overflow:hidden;">
|
|
102
|
-
<div style="width:50%;height:100%;background:#d29922;border-radius:2px;"></div>
|
|
103
|
-
</div>
|
|
104
|
-
<span style="font-size:11px;color:#f0f6fc;width:32px;text-align:right;font-weight:600;">50</span>
|
|
105
|
-
</div>
|
|
106
|
-
<div style="display:flex;align-items:center;gap:8px;margin:6px 0;">
|
|
107
|
-
<span style="width:120px;font-size:12px;color:#8b949e;">Vercel</span>
|
|
108
|
-
<div style="flex:1;height:4px;background:#21262d;border-radius:2px;overflow:hidden;">
|
|
109
|
-
<div style="width:48%;height:100%;background:#30363d;border-radius:2px;"></div>
|
|
110
|
-
</div>
|
|
111
|
-
<span style="font-size:11px;color:#8b949e;width:32px;text-align:right;">48</span>
|
|
112
|
-
</div>
|
|
113
|
-
<div style="display:flex;align-items:center;gap:8px;margin:6px 0;">
|
|
114
|
-
<span style="width:120px;font-size:12px;color:#8b949e;">Anthropic Docs</span>
|
|
115
|
-
<div style="flex:1;height:4px;background:#21262d;border-radius:2px;overflow:hidden;">
|
|
116
|
-
<div style="width:43%;height:100%;background:#30363d;border-radius:2px;"></div>
|
|
117
|
-
</div>
|
|
118
|
-
<span style="font-size:11px;color:#8b949e;width:32px;text-align:right;">43</span>
|
|
119
|
-
</div>
|
|
120
|
-
<div style="display:flex;align-items:center;gap:8px;margin:6px 0;">
|
|
121
|
-
<span style="width:120px;font-size:12px;color:#8b949e;">Stripe</span>
|
|
122
|
-
<div style="flex:1;height:4px;background:#21262d;border-radius:2px;overflow:hidden;">
|
|
123
|
-
<div style="width:33%;height:100%;background:#30363d;border-radius:2px;"></div>
|
|
124
|
-
</div>
|
|
125
|
-
<span style="font-size:11px;color:#8b949e;width:32px;text-align:right;">33</span>
|
|
126
|
-
</div>
|
|
127
|
-
<div style="display:flex;align-items:center;gap:8px;margin:6px 0;">
|
|
128
|
-
<span style="width:120px;font-size:12px;color:#8b949e;">Average site</span>
|
|
129
|
-
<div style="flex:1;height:4px;background:#21262d;border-radius:2px;overflow:hidden;">
|
|
130
|
-
<div style="width:25%;height:100%;background:#30363d;border-radius:2px;"></div>
|
|
131
|
-
</div>
|
|
132
|
-
<span style="font-size:11px;color:#8b949e;width:32px;text-align:right;">25</span>
|
|
133
|
-
</div>
|
|
134
|
-
</div>
|
|
135
|
-
<!-- /SECTION:overall-score -->
|
|
136
|
-
|
|
137
|
-
<!-- SECTION:agent-readiness-scorecard -->
|
|
138
|
-
<div class="scorecard" id="agent-readiness">
|
|
139
|
-
<div class="scorecard-header">
|
|
140
|
-
<h3>Agent Readiness</h3>
|
|
141
|
-
<span class="pct grade-c">22/50 (44%)</span>
|
|
142
|
-
</div>
|
|
143
|
-
<div class="category">
|
|
144
|
-
<div class="cat-header">
|
|
145
|
-
<span class="cat-name">Discovery</span>
|
|
146
|
-
<span class="cat-score">0/12</span>
|
|
147
|
-
<div class="cat-bar"><div class="cat-bar-fill" style="width:0%;background:#f85149"></div></div>
|
|
148
|
-
</div>
|
|
149
|
-
<div class="check fail">- llms.txt [0/4]<span class="fix">No llms.txt found. Create one describing what your site does for AI agents.</span></div>
|
|
150
|
-
<div class="check fail">- robots.txt AI crawlers [0/3]<span class="fix">No robots.txt found. Add one with explicit AI crawler rules.</span></div>
|
|
151
|
-
<div class="check fail">- sitemap.xml [0/2]<span class="fix">No sitemap.xml found.</span></div>
|
|
152
|
-
<div class="check fail">- AI-friendly meta tags [0/3]<span class="fix">No HTML available to check meta tags.</span></div>
|
|
153
|
-
</div>
|
|
154
|
-
<div class="category">
|
|
155
|
-
<div class="cat-header">
|
|
156
|
-
<span class="cat-name">Content Structure</span>
|
|
157
|
-
<span class="cat-score">10/15</span>
|
|
158
|
-
<div class="cat-bar"><div class="cat-bar-fill" style="width:67%;background:#d29922"></div></div>
|
|
159
|
-
</div>
|
|
160
|
-
<div class="check pass">+ Markdown content available [3]</div>
|
|
161
|
-
<div class="check pass">+ Heading hierarchy [3]</div>
|
|
162
|
-
<div class="check pass">+ Token budget compliance [4]</div>
|
|
163
|
-
<div class="check fail">- Front-loading (first 500 tokens) [1/3]<span class="fix">Most pages don't answer what/why/how in first 500 tokens. Lead with the point.</span></div>
|
|
164
|
-
<div class="check fail">- Copy-for-AI affordances [0/2]<span class="fix">No copy-for-AI affordances. Add a "Copy as Markdown" button on doc pages.</span></div>
|
|
165
|
-
</div>
|
|
166
|
-
<div class="category">
|
|
167
|
-
<div class="cat-header">
|
|
168
|
-
<span class="cat-name">Capability</span>
|
|
169
|
-
<span class="cat-score">4/13</span>
|
|
170
|
-
<div class="cat-bar"><div class="cat-bar-fill" style="width:31%;background:#f85149"></div></div>
|
|
171
|
-
</div>
|
|
172
|
-
<div class="check pass">+ AGENTS.md / CLAUDE.md [4]</div>
|
|
173
|
-
<div class="check fail">- agents.json manifest [0/3]<span class="fix">No agents.json or .well-known/agent.json found.</span></div>
|
|
174
|
-
<div class="check fail">- OpenAPI spec [0/3]<span class="fix">No OpenAPI spec found. Add openapi.json or openapi.yaml.</span></div>
|
|
175
|
-
<div class="check fail">- Content negotiation [0/3]<span class="fix">No content negotiation setup. Consider serving markdown for Accept: text/markdown requests.</span></div>
|
|
176
|
-
</div>
|
|
177
|
-
<div class="category">
|
|
178
|
-
<div class="cat-header">
|
|
179
|
-
<span class="cat-name">Actionable</span>
|
|
180
|
-
<span class="cat-score">8/10</span>
|
|
181
|
-
<div class="cat-bar"><div class="cat-bar-fill" style="width:80%;background:#3fb950"></div></div>
|
|
182
|
-
</div>
|
|
183
|
-
<div class="check pass">+ Machine-readable contact [3]</div>
|
|
184
|
-
<div class="check pass">+ Programmatic pricing [3]</div>
|
|
185
|
-
<div class="check fail">- API endpoint [1/2]<span class="fix">API references found but no formal spec. Add OpenAPI spec.</span></div>
|
|
186
|
-
<div class="check pass">+ SDK / integration manifest [2]</div>
|
|
187
|
-
</div>
|
|
188
|
-
</div>
|
|
189
|
-
<!-- /SECTION:agent-readiness-scorecard -->
|
|
190
|
-
|
|
191
|
-
<!-- SECTION:ai-visibility-scorecard -->
|
|
192
|
-
<div class="scorecard" id="ai-visibility">
|
|
193
|
-
<div class="scorecard-header">
|
|
194
|
-
<h3>AI Visibility</h3>
|
|
195
|
-
<span class="pct grade-c">28/50 (56%)</span>
|
|
196
|
-
</div>
|
|
197
|
-
<div class="category">
|
|
198
|
-
<div class="cat-header">
|
|
199
|
-
<span class="cat-name">Structured Data</span>
|
|
200
|
-
<span class="cat-score">6/12</span>
|
|
201
|
-
<div class="cat-bar"><div class="cat-bar-fill" style="width:50%;background:#d29922"></div></div>
|
|
202
|
-
</div>
|
|
203
|
-
<div class="check fail">- Schema.org markup [2/4]<span class="fix">Schema.org present but: type doesn't match site category, missing required fields.</span></div>
|
|
204
|
-
<div class="check pass">+ FAQ markup [3]</div>
|
|
205
|
-
<div class="check pass">+ Rich schemas (Breadcrumb/HowTo/Product) [3]</div>
|
|
206
|
-
<div class="check fail">- Open Graph + meta description [0/2]<span class="fix">No Open Graph or meta description tags. AI systems use these for page summaries.</span></div>
|
|
207
|
-
</div>
|
|
208
|
-
<div class="category">
|
|
209
|
-
<div class="cat-header">
|
|
210
|
-
<span class="cat-name">Citation Readiness</span>
|
|
211
|
-
<span class="cat-score">4/15</span>
|
|
212
|
-
<div class="cat-bar"><div class="cat-bar-fill" style="width:27%;background:#f85149"></div></div>
|
|
213
|
-
</div>
|
|
214
|
-
<div class="check fail">- Direct answer formatting [1/4]<span class="fix">Most pages don't lead with a concise summary (40-60 words). This is what AI systems cite.</span></div>
|
|
215
|
-
<div class="check fail">- Question-based headings [1/4]<span class="fix">Very few question-based headings. Rephrase key H2/H3s as questions users would ask AI.</span></div>
|
|
216
|
-
<div class="check pass">+ Citation-friendly structure [4]</div>
|
|
217
|
-
<div class="check fail">- Definitive statements [2/3]<span class="fix">Some definitive statements but too much hedging. AI cites confident claims over uncertain ones.</span></div>
|
|
218
|
-
</div>
|
|
219
|
-
<div class="category">
|
|
220
|
-
<div class="cat-header">
|
|
221
|
-
<span class="cat-name">Authority</span>
|
|
222
|
-
<span class="cat-score">10/13</span>
|
|
223
|
-
<div class="cat-bar"><div class="cat-bar-fill" style="width:77%;background:#d29922"></div></div>
|
|
224
|
-
</div>
|
|
225
|
-
<div class="check pass">+ E-E-A-T signals [4]</div>
|
|
226
|
-
<div class="check pass">+ Entity optimization [3]</div>
|
|
227
|
-
<div class="check pass">+ External validation & references [3]</div>
|
|
228
|
-
</div>
|
|
229
|
-
<div class="category">
|
|
230
|
-
<div class="cat-header">
|
|
231
|
-
<span class="cat-name">Freshness</span>
|
|
232
|
-
<span class="cat-score">8/10</span>
|
|
233
|
-
<div class="cat-bar"><div class="cat-bar-fill" style="width:80%;background:#3fb950"></div></div>
|
|
234
|
-
</div>
|
|
235
|
-
<div class="check pass">+ Last modified dates [3]</div>
|
|
236
|
-
<div class="check fail">- Publication cadence [0/2]<span class="fix">Infrequent updates (~541 day gaps). Regular publishing signals authority to AI systems.</span></div>
|
|
237
|
-
<div class="check pass">+ Content recency [3]</div>
|
|
238
|
-
<div class="check pass">+ Evergreen content flagged [2]</div>
|
|
239
|
-
</div>
|
|
240
|
-
</div>
|
|
241
|
-
<!-- /SECTION:ai-visibility-scorecard -->
|
|
242
|
-
|
|
243
|
-
<!-- SECTION:trend-chart -->
|
|
244
|
-
<h2 id="trends">Score Trends</h2>
|
|
245
|
-
<svg width="700" height="200" xmlns="http://www.w3.org/2000/svg">
|
|
246
|
-
<line x1="40" y1="170" x2="680" y2="170" stroke="#21262d" stroke-width="1"/>
|
|
247
|
-
<text x="32" y="174" text-anchor="end" fill="#8b949e" font-size="10">0</text>
|
|
248
|
-
<line x1="40" y1="132.5" x2="680" y2="132.5" stroke="#21262d" stroke-width="1"/>
|
|
249
|
-
<text x="32" y="136.5" text-anchor="end" fill="#8b949e" font-size="10">25</text>
|
|
250
|
-
<line x1="40" y1="95" x2="680" y2="95" stroke="#21262d" stroke-width="1"/>
|
|
251
|
-
<text x="32" y="99" text-anchor="end" fill="#8b949e" font-size="10">50</text>
|
|
252
|
-
<line x1="40" y1="57.5" x2="680" y2="57.5" stroke="#21262d" stroke-width="1"/>
|
|
253
|
-
<text x="32" y="61.5" text-anchor="end" fill="#8b949e" font-size="10">75</text>
|
|
254
|
-
<line x1="40" y1="20" x2="680" y2="20" stroke="#21262d" stroke-width="1"/>
|
|
255
|
-
<text x="32" y="24" text-anchor="end" fill="#8b949e" font-size="10">100</text>
|
|
256
|
-
<text x="40" y="195" text-anchor="middle" fill="#8b949e" font-size="10">05-18</text>
|
|
257
|
-
<text x="680" y="195" text-anchor="middle" fill="#8b949e" font-size="10">05-18</text>
|
|
258
|
-
<path d="M 40.0 80.0 L 680.0 104.0" fill="none" stroke="#79c0ff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
259
|
-
<path d="M 40.0 89.0 L 680.0 86.0" fill="none" stroke="#d29922" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
260
|
-
<path d="M 40.0 84.5 L 680.0 95.0" fill="none" stroke="#f0f6fc" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
261
|
-
<circle cx="40.0" cy="84.5" r="3" fill="#f0f6fc"/>
|
|
262
|
-
<circle cx="680.0" cy="95.0" r="3" fill="#f0f6fc"/>
|
|
263
|
-
<text x="680" y="15" text-anchor="end" fill="#f0f6fc" font-size="10">Overall</text>
|
|
264
|
-
<text x="600" y="15" text-anchor="end" fill="#79c0ff" font-size="10">Agent (x2)</text>
|
|
265
|
-
<text x="500" y="15" text-anchor="end" fill="#d29922" font-size="10">Visibility (x2)</text>
|
|
266
|
-
</svg>
|
|
267
|
-
<!-- /SECTION:trend-chart -->
|
|
268
|
-
|
|
269
|
-
<!-- SECTION:history-table -->
|
|
270
|
-
<h2 id="history">Scan History</h2>
|
|
271
|
-
<table>
|
|
272
|
-
<tr><th>Date</th><th>Score</th><th>Grade</th><th>Agent</th><th>Visibility</th><th>Delta</th></tr>
|
|
273
|
-
<tr>
|
|
274
|
-
<td>2026-05-18</td>
|
|
275
|
-
<td>50/100</td>
|
|
276
|
-
<td class="grade-c">C</td>
|
|
277
|
-
<td>22/50</td>
|
|
278
|
-
<td>28/50</td>
|
|
279
|
-
<td><span class="regression">-7</span></td>
|
|
280
|
-
</tr>
|
|
281
|
-
<tr>
|
|
282
|
-
<td>2026-05-18</td>
|
|
283
|
-
<td>57/100</td>
|
|
284
|
-
<td class="grade-c">C</td>
|
|
285
|
-
<td>30/50</td>
|
|
286
|
-
<td>27/50</td>
|
|
287
|
-
<td><span style="color:#8b949e">—</span></td>
|
|
288
|
-
</tr>
|
|
289
|
-
</table>
|
|
290
|
-
<!-- /SECTION:history-table -->
|
|
291
|
-
|
|
292
|
-
<!-- SECTION:recommendations -->
|
|
293
|
-
<h2 id="recommendations">Top Recommendations</h2>
|
|
294
|
-
<p style="color:#8b949e;font-size:12px;margin-bottom:12px;">Sorted by point impact — fix these first.</p>
|
|
295
|
-
<div class="rec">
|
|
296
|
-
<div class="rec-title">llms.txt</div>
|
|
297
|
-
<div class="rec-fix">No llms.txt found. Create one describing what your site does for AI agents.</div>
|
|
298
|
-
<div class="rec-impact">+4 points · Discovery</div>
|
|
299
|
-
</div>
|
|
300
|
-
<div class="rec">
|
|
301
|
-
<div class="rec-title">robots.txt AI crawlers</div>
|
|
302
|
-
<div class="rec-fix">No robots.txt found. Add one with explicit AI crawler rules.</div>
|
|
303
|
-
<div class="rec-impact">+3 points · Discovery</div>
|
|
304
|
-
</div>
|
|
305
|
-
<div class="rec">
|
|
306
|
-
<div class="rec-title">AI-friendly meta tags</div>
|
|
307
|
-
<div class="rec-fix">No HTML available to check meta tags.</div>
|
|
308
|
-
<div class="rec-impact">+3 points · Discovery</div>
|
|
309
|
-
</div>
|
|
310
|
-
<div class="rec">
|
|
311
|
-
<div class="rec-title">agents.json manifest</div>
|
|
312
|
-
<div class="rec-fix">No agents.json or .well-known/agent.json found.</div>
|
|
313
|
-
<div class="rec-impact">+3 points · Capability</div>
|
|
314
|
-
</div>
|
|
315
|
-
<div class="rec">
|
|
316
|
-
<div class="rec-title">OpenAPI spec</div>
|
|
317
|
-
<div class="rec-fix">No OpenAPI spec found. Add openapi.json or openapi.yaml.</div>
|
|
318
|
-
<div class="rec-impact">+3 points · Capability</div>
|
|
319
|
-
</div>
|
|
320
|
-
<div class="rec">
|
|
321
|
-
<div class="rec-title">Content negotiation</div>
|
|
322
|
-
<div class="rec-fix">No content negotiation setup. Consider serving markdown for Accept: text/markdown requests.</div>
|
|
323
|
-
<div class="rec-impact">+3 points · Capability</div>
|
|
324
|
-
</div>
|
|
325
|
-
<div class="rec">
|
|
326
|
-
<div class="rec-title">Direct answer formatting</div>
|
|
327
|
-
<div class="rec-fix">Most pages don't lead with a concise summary (40-60 words). This is what AI systems cite.</div>
|
|
328
|
-
<div class="rec-impact">+3 points · Citation Readiness</div>
|
|
329
|
-
</div>
|
|
330
|
-
<div class="rec">
|
|
331
|
-
<div class="rec-title">Question-based headings</div>
|
|
332
|
-
<div class="rec-fix">Very few question-based headings. Rephrase key H2/H3s as questions users would ask AI.</div>
|
|
333
|
-
<div class="rec-impact">+3 points · Citation Readiness</div>
|
|
334
|
-
</div>
|
|
335
|
-
<!-- /SECTION:recommendations -->
|
|
336
|
-
|
|
337
|
-
</main>
|
|
338
|
-
</body>
|
|
339
|
-
</html>
|
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
export async function runActionableChecks(context) {
|
|
2
|
-
const checks = [
|
|
3
|
-
checkContact(context),
|
|
4
|
-
checkPricing(context),
|
|
5
|
-
checkApiEndpoint(context),
|
|
6
|
-
checkSdkManifest(context),
|
|
7
|
-
];
|
|
8
|
-
|
|
9
|
-
const score = checks.reduce((sum, c) => sum + (c.passed ? c.points : 0), 0);
|
|
10
|
-
return { score, maxScore: 10, checks };
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
function checkContact(context) {
|
|
14
|
-
const html = context.html || "";
|
|
15
|
-
const allContent = getAllContent(context);
|
|
16
|
-
|
|
17
|
-
const hasEmail = /[\w.+-]+@[\w-]+\.[\w.]+/.test(allContent);
|
|
18
|
-
const hasCalLink = /cal\.com|calendly\.com|hubspot\.com\/meetings/.test(
|
|
19
|
-
allContent,
|
|
20
|
-
);
|
|
21
|
-
const hasStructuredContact =
|
|
22
|
-
allContent.includes("ContactPoint") || allContent.includes('"email"');
|
|
23
|
-
|
|
24
|
-
let points = 0;
|
|
25
|
-
if (hasEmail) points += 1;
|
|
26
|
-
if (hasCalLink) points += 1;
|
|
27
|
-
if (hasStructuredContact) points += 1;
|
|
28
|
-
|
|
29
|
-
if (points === 3) return pass("Machine-readable contact", 3);
|
|
30
|
-
if (points > 0) {
|
|
31
|
-
const missing = [];
|
|
32
|
-
if (!hasEmail) missing.push("email");
|
|
33
|
-
if (!hasCalLink) missing.push("booking link");
|
|
34
|
-
if (!hasStructuredContact) missing.push("structured ContactPoint");
|
|
35
|
-
return partial(
|
|
36
|
-
"Machine-readable contact",
|
|
37
|
-
points,
|
|
38
|
-
3,
|
|
39
|
-
`Add: ${missing.join(", ")}.`,
|
|
40
|
-
);
|
|
41
|
-
}
|
|
42
|
-
return fail(
|
|
43
|
-
"Machine-readable contact",
|
|
44
|
-
3,
|
|
45
|
-
"No machine-readable contact info. Add email, cal.com link, or ContactPoint schema.",
|
|
46
|
-
);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function checkPricing(context) {
|
|
50
|
-
if (context.siteType === "personal" || context.siteType === "content") {
|
|
51
|
-
return {
|
|
52
|
-
name: "Programmatic pricing",
|
|
53
|
-
passed: true,
|
|
54
|
-
points: 3,
|
|
55
|
-
maxPoints: 3,
|
|
56
|
-
note: "N/A for site type",
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const allContent = getAllContent(context);
|
|
61
|
-
const files = context.files || [];
|
|
62
|
-
|
|
63
|
-
const hasPricingJson = files.some((f) => f.includes("pricing.json"));
|
|
64
|
-
const hasPriceSchema =
|
|
65
|
-
allContent.includes("PriceSpecification") || allContent.includes("offers");
|
|
66
|
-
const hasPricingPage =
|
|
67
|
-
allContent.includes("/pricing") || allContent.includes("pricing");
|
|
68
|
-
|
|
69
|
-
if (hasPricingJson || hasPriceSchema) return pass("Programmatic pricing", 3);
|
|
70
|
-
if (hasPricingPage)
|
|
71
|
-
return partial(
|
|
72
|
-
"Programmatic pricing",
|
|
73
|
-
1,
|
|
74
|
-
3,
|
|
75
|
-
"Pricing page exists but not in structured format. Add schema.org PriceSpecification or pricing.json.",
|
|
76
|
-
);
|
|
77
|
-
return fail(
|
|
78
|
-
"Programmatic pricing",
|
|
79
|
-
3,
|
|
80
|
-
"No programmatic pricing. Agents comparing you to competitors need structured plan data.",
|
|
81
|
-
);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function checkApiEndpoint(context) {
|
|
85
|
-
if (context.siteType === "personal") {
|
|
86
|
-
return {
|
|
87
|
-
name: "API endpoint",
|
|
88
|
-
passed: true,
|
|
89
|
-
points: 2,
|
|
90
|
-
maxPoints: 2,
|
|
91
|
-
note: "N/A for site type",
|
|
92
|
-
};
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
const allContent = getAllContent(context);
|
|
96
|
-
const hasApiRef = /\/api\/|\/v[12]\/|api-key|apikey/i.test(allContent);
|
|
97
|
-
const hasOpenApi =
|
|
98
|
-
context.files?.some((f) => f.includes("openapi")) ||
|
|
99
|
-
context.pages?.openapi?.status === 200;
|
|
100
|
-
|
|
101
|
-
if (hasApiRef && hasOpenApi) return pass("API endpoint", 2);
|
|
102
|
-
if (hasApiRef)
|
|
103
|
-
return partial(
|
|
104
|
-
"API endpoint",
|
|
105
|
-
1,
|
|
106
|
-
2,
|
|
107
|
-
"API references found but no formal spec. Add OpenAPI spec.",
|
|
108
|
-
);
|
|
109
|
-
return fail("API endpoint", 2, "No API endpoint detected.");
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
function checkSdkManifest(context) {
|
|
113
|
-
if (context.siteType === "personal" || context.siteType === "content") {
|
|
114
|
-
return {
|
|
115
|
-
name: "SDK / integration manifest",
|
|
116
|
-
passed: true,
|
|
117
|
-
points: 2,
|
|
118
|
-
maxPoints: 2,
|
|
119
|
-
note: "N/A for site type",
|
|
120
|
-
};
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
const allContent = getAllContent(context);
|
|
124
|
-
const hasSdk = /npm install|pip install|go get|cargo add|maven|gradle/i.test(
|
|
125
|
-
allContent,
|
|
126
|
-
);
|
|
127
|
-
const hasIntegrations = /integrat|connect|plugin|extension|mcp/i.test(
|
|
128
|
-
allContent,
|
|
129
|
-
);
|
|
130
|
-
|
|
131
|
-
if (hasSdk) return pass("SDK / integration manifest", 2);
|
|
132
|
-
if (hasIntegrations)
|
|
133
|
-
return partial(
|
|
134
|
-
"SDK / integration manifest",
|
|
135
|
-
1,
|
|
136
|
-
2,
|
|
137
|
-
"Integration mentions found but no install commands or SDK manifest.",
|
|
138
|
-
);
|
|
139
|
-
return fail(
|
|
140
|
-
"SDK / integration manifest",
|
|
141
|
-
2,
|
|
142
|
-
"No SDK or integration manifest detected.",
|
|
143
|
-
);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
function getAllContent(context) {
|
|
147
|
-
if (context.mode === "url") {
|
|
148
|
-
return Object.values(context.pages || {})
|
|
149
|
-
.map((p) => p?.text || "")
|
|
150
|
-
.join("\n");
|
|
151
|
-
}
|
|
152
|
-
return Object.values(context.fileContents || {}).join("\n");
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
function pass(name, points) {
|
|
156
|
-
return { name, passed: true, points, maxPoints: points };
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
function partial(name, points, maxPoints, fix) {
|
|
160
|
-
return { name, passed: false, points, maxPoints, fix };
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
function fail(name, maxPoints, fix) {
|
|
164
|
-
return { name, passed: false, points: 0, maxPoints, fix };
|
|
165
|
-
}
|