aeoptimize 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +19 -0
- package/.claude-plugin/plugin.json +12 -0
- package/README.md +106 -0
- package/agents/aeo-analyzer.md +63 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +224 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/core/generator.d.ts +6 -0
- package/dist/core/generator.js +126 -0
- package/dist/core/generator.js.map +1 -0
- package/dist/core/index.d.ts +4 -0
- package/dist/core/index.js +5 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/rules.d.ts +3 -0
- package/dist/core/rules.js +609 -0
- package/dist/core/rules.js.map +1 -0
- package/dist/core/scanner.d.ts +8 -0
- package/dist/core/scanner.js +290 -0
- package/dist/core/scanner.js.map +1 -0
- package/dist/core/types.d.ts +87 -0
- package/dist/core/types.js +2 -0
- package/dist/core/types.js.map +1 -0
- package/package.json +54 -0
- package/skills/aeo-generate/SKILL.md +53 -0
- package/skills/aeo-scan/SKILL.md +50 -0
- package/skills/aeo-transform/SKILL.md +84 -0
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
import * as cheerio from 'cheerio';
|
|
2
|
+
import matter from 'gray-matter';
|
|
3
|
+
import { readFile, readdir, stat } from 'node:fs/promises';
|
|
4
|
+
import { join, extname } from 'node:path';
|
|
5
|
+
import { allRules } from './rules.js';
|
|
6
|
+
// ── Parsers ────────────────────────────────────────────────────────
|
|
7
|
+
export function parseHtml(html, url) {
|
|
8
|
+
const $ = cheerio.load(html);
|
|
9
|
+
// Extract title
|
|
10
|
+
const title = $('title').first().text().trim() || $('h1').first().text().trim() || url;
|
|
11
|
+
// Extract headings
|
|
12
|
+
const headings = [];
|
|
13
|
+
$('h1, h2, h3, h4, h5, h6').each((_, el) => {
|
|
14
|
+
const tag = el.tagName;
|
|
15
|
+
headings.push({
|
|
16
|
+
level: parseInt(tag.charAt(1), 10),
|
|
17
|
+
text: $(el).text().trim(),
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
// Extract paragraphs
|
|
21
|
+
const paragraphs = [];
|
|
22
|
+
$('p').each((_, el) => {
|
|
23
|
+
const text = $(el).text().trim();
|
|
24
|
+
if (text.length > 0)
|
|
25
|
+
paragraphs.push(text);
|
|
26
|
+
});
|
|
27
|
+
// Extract JSON-LD
|
|
28
|
+
const jsonLd = [];
|
|
29
|
+
$('script[type="application/ld+json"]').each((_, el) => {
|
|
30
|
+
try {
|
|
31
|
+
const parsed = JSON.parse($(el).html() || '');
|
|
32
|
+
if (Array.isArray(parsed)) {
|
|
33
|
+
jsonLd.push(...parsed);
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
jsonLd.push(parsed);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
// Ignore malformed JSON-LD
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
// Extract meta tags
|
|
44
|
+
const metaTags = {};
|
|
45
|
+
$('meta').each((_, el) => {
|
|
46
|
+
const name = $(el).attr('name') || $(el).attr('property') || '';
|
|
47
|
+
const content = $(el).attr('content') || '';
|
|
48
|
+
if (name && content)
|
|
49
|
+
metaTags[name] = content;
|
|
50
|
+
});
|
|
51
|
+
// Extract links
|
|
52
|
+
const links = [];
|
|
53
|
+
$('a').each((_, el) => {
|
|
54
|
+
const href = $(el).attr('href') || '';
|
|
55
|
+
const text = $(el).text().trim();
|
|
56
|
+
const rel = $(el).attr('rel') || undefined;
|
|
57
|
+
if (href)
|
|
58
|
+
links.push({ href, text, rel });
|
|
59
|
+
});
|
|
60
|
+
// Extract raw text (content area preferred)
|
|
61
|
+
const contentArea = $('main, article, [role="main"]').first();
|
|
62
|
+
const rawText = (contentArea.length > 0 ? contentArea.text() : $('body').text()).replace(/\s+/g, ' ').trim();
|
|
63
|
+
return {
|
|
64
|
+
url,
|
|
65
|
+
title,
|
|
66
|
+
html,
|
|
67
|
+
headings,
|
|
68
|
+
paragraphs,
|
|
69
|
+
jsonLd,
|
|
70
|
+
metaTags,
|
|
71
|
+
links,
|
|
72
|
+
rawText,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
export function parseMarkdown(md, url) {
|
|
76
|
+
const { data: frontmatter, content } = matter(md);
|
|
77
|
+
const title = frontmatter.title || '';
|
|
78
|
+
// Extract headings from markdown
|
|
79
|
+
const headings = [];
|
|
80
|
+
const headingRegex = /^(#{1,6})\s+(.+)$/gm;
|
|
81
|
+
let match;
|
|
82
|
+
while ((match = headingRegex.exec(content)) !== null) {
|
|
83
|
+
headings.push({
|
|
84
|
+
level: match[1].length,
|
|
85
|
+
text: match[2].trim(),
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
// Set title from first H1 if not in frontmatter
|
|
89
|
+
const finalTitle = title || headings.find((h) => h.level === 1)?.text || url;
|
|
90
|
+
// Extract paragraphs (non-empty lines that aren't headings, lists, or code blocks)
|
|
91
|
+
const paragraphs = [];
|
|
92
|
+
let inCodeBlock = false;
|
|
93
|
+
const lines = content.split('\n');
|
|
94
|
+
let currentParagraph = '';
|
|
95
|
+
for (const line of lines) {
|
|
96
|
+
if (line.trim().startsWith('```')) {
|
|
97
|
+
inCodeBlock = !inCodeBlock;
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
if (inCodeBlock)
|
|
101
|
+
continue;
|
|
102
|
+
const trimmed = line.trim();
|
|
103
|
+
if (trimmed === '') {
|
|
104
|
+
if (currentParagraph.length > 0) {
|
|
105
|
+
paragraphs.push(currentParagraph.trim());
|
|
106
|
+
currentParagraph = '';
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
else if (!trimmed.startsWith('#') && !trimmed.startsWith('-') && !trimmed.startsWith('*') && !trimmed.match(/^\d+\./)) {
|
|
110
|
+
currentParagraph += ' ' + trimmed;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
if (currentParagraph.trim().length > 0) {
|
|
114
|
+
paragraphs.push(currentParagraph.trim());
|
|
115
|
+
}
|
|
116
|
+
// Raw text (strip markdown syntax roughly)
|
|
117
|
+
const rawText = content
|
|
118
|
+
.replace(/```[\s\S]*?```/g, '')
|
|
119
|
+
.replace(/`[^`]+`/g, '')
|
|
120
|
+
.replace(/!\[.*?\]\(.*?\)/g, '')
|
|
121
|
+
.replace(/\[([^\]]+)\]\(.*?\)/g, '$1')
|
|
122
|
+
.replace(/[#*_~`>]/g, '')
|
|
123
|
+
.replace(/\s+/g, ' ')
|
|
124
|
+
.trim();
|
|
125
|
+
return {
|
|
126
|
+
url,
|
|
127
|
+
title: finalTitle,
|
|
128
|
+
markdown: md,
|
|
129
|
+
frontmatter: frontmatter,
|
|
130
|
+
headings,
|
|
131
|
+
paragraphs,
|
|
132
|
+
jsonLd: [],
|
|
133
|
+
metaTags: {},
|
|
134
|
+
links: [],
|
|
135
|
+
rawText,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
// ── Scoring ────────────────────────────────────────────────────────
|
|
139
|
+
function aggregateScores(doc) {
|
|
140
|
+
const dimensionTotals = {
|
|
141
|
+
structure: { score: 0, maxScore: 0 },
|
|
142
|
+
citability: { score: 0, maxScore: 0 },
|
|
143
|
+
schema: { score: 0, maxScore: 0 },
|
|
144
|
+
aiMetadata: { score: 0, maxScore: 0 },
|
|
145
|
+
contentDensity: { score: 0, maxScore: 0 },
|
|
146
|
+
};
|
|
147
|
+
const allIssues = [];
|
|
148
|
+
const allSuggestions = [];
|
|
149
|
+
for (const rule of allRules) {
|
|
150
|
+
const result = rule.evaluate(doc);
|
|
151
|
+
dimensionTotals[rule.dimension].score += result.score;
|
|
152
|
+
dimensionTotals[rule.dimension].maxScore += result.maxScore;
|
|
153
|
+
allIssues.push(...result.issues);
|
|
154
|
+
allSuggestions.push(...result.suggestions);
|
|
155
|
+
}
|
|
156
|
+
// Dimension max points
|
|
157
|
+
const dimensionMaxPoints = {
|
|
158
|
+
structure: 25,
|
|
159
|
+
citability: 25,
|
|
160
|
+
schema: 20,
|
|
161
|
+
aiMetadata: 15,
|
|
162
|
+
contentDensity: 15,
|
|
163
|
+
};
|
|
164
|
+
const scores = {
|
|
165
|
+
structure: 0,
|
|
166
|
+
citability: 0,
|
|
167
|
+
schema: 0,
|
|
168
|
+
aiMetadata: 0,
|
|
169
|
+
contentDensity: 0,
|
|
170
|
+
total: 0,
|
|
171
|
+
};
|
|
172
|
+
for (const dim of Object.keys(dimensionTotals)) {
|
|
173
|
+
const { score, maxScore } = dimensionTotals[dim];
|
|
174
|
+
scores[dim] = maxScore > 0 ? Math.round((score / maxScore) * dimensionMaxPoints[dim]) : 0;
|
|
175
|
+
}
|
|
176
|
+
scores.total = scores.structure + scores.citability + scores.schema + scores.aiMetadata + scores.contentDensity;
|
|
177
|
+
return { scores, issues: allIssues, suggestions: allSuggestions };
|
|
178
|
+
}
|
|
179
|
+
// ── Public API ─────────────────────────────────────────────────────
|
|
180
|
+
export function scanDocument(doc) {
|
|
181
|
+
const { scores, issues, suggestions } = aggregateScores(doc);
|
|
182
|
+
return {
|
|
183
|
+
url: doc.url,
|
|
184
|
+
title: doc.title,
|
|
185
|
+
scores,
|
|
186
|
+
issues,
|
|
187
|
+
suggestions,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
export async function scanFile(filePath) {
|
|
191
|
+
const content = await readFile(filePath, 'utf-8');
|
|
192
|
+
const ext = extname(filePath).toLowerCase();
|
|
193
|
+
let doc;
|
|
194
|
+
if (ext === '.html' || ext === '.htm') {
|
|
195
|
+
doc = parseHtml(content, filePath);
|
|
196
|
+
}
|
|
197
|
+
else if (ext === '.md' || ext === '.mdx') {
|
|
198
|
+
doc = parseMarkdown(content, filePath);
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
throw new Error(`Unsupported file type: ${ext}`);
|
|
202
|
+
}
|
|
203
|
+
return scanDocument(doc);
|
|
204
|
+
}
|
|
205
|
+
export async function scanDirectory(dirPath) {
|
|
206
|
+
const pages = [];
|
|
207
|
+
await walkDir(dirPath, pages);
|
|
208
|
+
if (pages.length === 0) {
|
|
209
|
+
return {
|
|
210
|
+
pages: [],
|
|
211
|
+
overall: { structure: 0, citability: 0, schema: 0, aiMetadata: 0, contentDensity: 0, total: 0 },
|
|
212
|
+
summary: 'No HTML or Markdown files found in directory.',
|
|
213
|
+
timestamp: new Date().toISOString(),
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
const overall = averageScores(pages.map((p) => p.scores));
|
|
217
|
+
const summary = generateSummary(overall, pages.length);
|
|
218
|
+
return { pages, overall, summary, timestamp: new Date().toISOString() };
|
|
219
|
+
}
|
|
220
|
+
export async function scanUrl(url) {
|
|
221
|
+
const response = await fetch(url);
|
|
222
|
+
if (!response.ok) {
|
|
223
|
+
throw new Error(`Failed to fetch ${url}: ${response.status} ${response.statusText}`);
|
|
224
|
+
}
|
|
225
|
+
const html = await response.text();
|
|
226
|
+
const doc = parseHtml(html, url);
|
|
227
|
+
const analysis = scanDocument(doc);
|
|
228
|
+
return {
|
|
229
|
+
pages: [analysis],
|
|
230
|
+
overall: analysis.scores,
|
|
231
|
+
summary: generateSummary(analysis.scores, 1),
|
|
232
|
+
timestamp: new Date().toISOString(),
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
export async function scan(target) {
|
|
236
|
+
switch (target.type) {
|
|
237
|
+
case 'url':
|
|
238
|
+
return scanUrl(target.path);
|
|
239
|
+
case 'file': {
|
|
240
|
+
const analysis = await scanFile(target.path);
|
|
241
|
+
return {
|
|
242
|
+
pages: [analysis],
|
|
243
|
+
overall: analysis.scores,
|
|
244
|
+
summary: generateSummary(analysis.scores, 1),
|
|
245
|
+
timestamp: new Date().toISOString(),
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
case 'directory':
|
|
249
|
+
return scanDirectory(target.path);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
// ── Helpers ────────────────────────────────────────────────────────
|
|
253
|
+
async function walkDir(dirPath, pages) {
|
|
254
|
+
const entries = await readdir(dirPath);
|
|
255
|
+
for (const entry of entries) {
|
|
256
|
+
const fullPath = join(dirPath, entry);
|
|
257
|
+
const stats = await stat(fullPath);
|
|
258
|
+
if (stats.isDirectory()) {
|
|
259
|
+
if (entry.startsWith('.') || entry === 'node_modules')
|
|
260
|
+
continue;
|
|
261
|
+
await walkDir(fullPath, pages);
|
|
262
|
+
}
|
|
263
|
+
else {
|
|
264
|
+
const ext = extname(entry).toLowerCase();
|
|
265
|
+
if (['.html', '.htm', '.md', '.mdx'].includes(ext)) {
|
|
266
|
+
try {
|
|
267
|
+
const analysis = await scanFile(fullPath);
|
|
268
|
+
pages.push(analysis);
|
|
269
|
+
}
|
|
270
|
+
catch {
|
|
271
|
+
// Skip files that can't be parsed
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
function averageScores(scores) {
|
|
278
|
+
const avg = { structure: 0, citability: 0, schema: 0, aiMetadata: 0, contentDensity: 0, total: 0 };
|
|
279
|
+
const dims = ['structure', 'citability', 'schema', 'aiMetadata', 'contentDensity'];
|
|
280
|
+
for (const dim of dims) {
|
|
281
|
+
avg[dim] = Math.round(scores.reduce((sum, s) => sum + s[dim], 0) / scores.length);
|
|
282
|
+
}
|
|
283
|
+
avg.total = avg.structure + avg.citability + avg.schema + avg.aiMetadata + avg.contentDensity;
|
|
284
|
+
return avg;
|
|
285
|
+
}
|
|
286
|
+
function generateSummary(scores, pageCount) {
|
|
287
|
+
const grade = scores.total >= 80 ? 'Excellent' : scores.total >= 60 ? 'Good' : scores.total >= 40 ? 'Needs Work' : 'Poor';
|
|
288
|
+
return `AI Readability: ${grade} (${scores.total}/100) across ${pageCount} page${pageCount > 1 ? 's' : ''}`;
|
|
289
|
+
}
|
|
290
|
+
//# sourceMappingURL=scanner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scanner.js","sourceRoot":"","sources":["../../src/core/scanner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
export type Dimension = 'structure' | 'citability' | 'schema' | 'aiMetadata' | 'contentDensity';
|
|
2
|
+
export interface DimensionScores {
|
|
3
|
+
structure: number;
|
|
4
|
+
citability: number;
|
|
5
|
+
schema: number;
|
|
6
|
+
aiMetadata: number;
|
|
7
|
+
contentDensity: number;
|
|
8
|
+
total: number;
|
|
9
|
+
}
|
|
10
|
+
export type Severity = 'critical' | 'warning' | 'info';
|
|
11
|
+
export type Impact = 'high' | 'medium' | 'low';
|
|
12
|
+
export interface Issue {
|
|
13
|
+
dimension: Dimension;
|
|
14
|
+
severity: Severity;
|
|
15
|
+
message: string;
|
|
16
|
+
selector?: string;
|
|
17
|
+
line?: number;
|
|
18
|
+
}
|
|
19
|
+
export interface Suggestion {
|
|
20
|
+
dimension: Dimension;
|
|
21
|
+
action: string;
|
|
22
|
+
impact: Impact;
|
|
23
|
+
detail: string;
|
|
24
|
+
}
|
|
25
|
+
export interface ScanTarget {
|
|
26
|
+
type: 'url' | 'file' | 'directory';
|
|
27
|
+
path: string;
|
|
28
|
+
}
|
|
29
|
+
export interface Heading {
|
|
30
|
+
level: number;
|
|
31
|
+
text: string;
|
|
32
|
+
}
|
|
33
|
+
export interface Link {
|
|
34
|
+
href: string;
|
|
35
|
+
text: string;
|
|
36
|
+
rel?: string;
|
|
37
|
+
}
|
|
38
|
+
export interface ParsedDocument {
|
|
39
|
+
url: string;
|
|
40
|
+
title: string;
|
|
41
|
+
html?: string;
|
|
42
|
+
markdown?: string;
|
|
43
|
+
frontmatter?: Record<string, unknown>;
|
|
44
|
+
headings: Heading[];
|
|
45
|
+
paragraphs: string[];
|
|
46
|
+
jsonLd: object[];
|
|
47
|
+
metaTags: Record<string, string>;
|
|
48
|
+
links: Link[];
|
|
49
|
+
rawText: string;
|
|
50
|
+
}
|
|
51
|
+
export interface RuleResult {
|
|
52
|
+
score: number;
|
|
53
|
+
maxScore: number;
|
|
54
|
+
issues: Issue[];
|
|
55
|
+
suggestions: Suggestion[];
|
|
56
|
+
}
|
|
57
|
+
export interface ScoringRule {
|
|
58
|
+
id: string;
|
|
59
|
+
dimension: Dimension;
|
|
60
|
+
weight: number;
|
|
61
|
+
evaluate: (doc: ParsedDocument) => RuleResult;
|
|
62
|
+
}
|
|
63
|
+
export interface PageAnalysis {
|
|
64
|
+
url: string;
|
|
65
|
+
title: string;
|
|
66
|
+
scores: DimensionScores;
|
|
67
|
+
issues: Issue[];
|
|
68
|
+
suggestions: Suggestion[];
|
|
69
|
+
}
|
|
70
|
+
export interface ScanReport {
|
|
71
|
+
pages: PageAnalysis[];
|
|
72
|
+
overall: DimensionScores;
|
|
73
|
+
summary: string;
|
|
74
|
+
timestamp: string;
|
|
75
|
+
}
|
|
76
|
+
export interface SiteInfo {
|
|
77
|
+
name: string;
|
|
78
|
+
description: string;
|
|
79
|
+
baseUrl: string;
|
|
80
|
+
language?: string;
|
|
81
|
+
}
|
|
82
|
+
export interface GenerateOutput {
|
|
83
|
+
llmsTxt: string;
|
|
84
|
+
llmsFullTxt: string;
|
|
85
|
+
jsonLd: object[];
|
|
86
|
+
robotsTxtSuggestions: string[];
|
|
87
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "aeoptimize",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "CLI toolkit that transforms SEO-optimized websites into AI-search-ready content",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/core/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"aeo": "./dist/cli/index.js",
|
|
9
|
+
"aeo-cli": "./dist/cli/index.js"
|
|
10
|
+
},
|
|
11
|
+
"exports": {
|
|
12
|
+
".": "./dist/core/index.js"
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist/",
|
|
16
|
+
"skills/",
|
|
17
|
+
"agents/",
|
|
18
|
+
".claude-plugin/",
|
|
19
|
+
"README.md",
|
|
20
|
+
"LICENSE"
|
|
21
|
+
],
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "tsc",
|
|
24
|
+
"dev": "tsc --watch",
|
|
25
|
+
"test": "vitest run",
|
|
26
|
+
"test:watch": "vitest",
|
|
27
|
+
"prepublishOnly": "npm test && npm run build"
|
|
28
|
+
},
|
|
29
|
+
"keywords": [
|
|
30
|
+
"aeo",
|
|
31
|
+
"seo",
|
|
32
|
+
"ai-search",
|
|
33
|
+
"llms-txt",
|
|
34
|
+
"structured-data",
|
|
35
|
+
"json-ld",
|
|
36
|
+
"generative-engine-optimization",
|
|
37
|
+
"claude-code-skill"
|
|
38
|
+
],
|
|
39
|
+
"license": "MIT",
|
|
40
|
+
"engines": {
|
|
41
|
+
"node": ">=18.0.0"
|
|
42
|
+
},
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"chalk": "^5.3.0",
|
|
45
|
+
"cheerio": "^1.0.0",
|
|
46
|
+
"commander": "^12.0.0",
|
|
47
|
+
"gray-matter": "^4.0.3"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@types/node": "^20.0.0",
|
|
51
|
+
"typescript": "^5.4.0",
|
|
52
|
+
"vitest": "^2.0.0"
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: aeo-generate
|
|
3
|
+
description: Use when creating llms.txt, JSON-LD structured data, or robots.txt AI crawler configuration for a website or project build output
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# AEO Generate — AI Infrastructure Files
|
|
7
|
+
|
|
8
|
+
Generate AI infrastructure files from existing website content to make it discoverable by AI search engines.
|
|
9
|
+
|
|
10
|
+
## What Gets Generated
|
|
11
|
+
|
|
12
|
+
| File | Purpose |
|
|
13
|
+
|------|---------|
|
|
14
|
+
| `llms.txt` | Machine-readable site summary for LLMs (llmstxt.org standard) |
|
|
15
|
+
| `llms-full.txt` | Full content version for deep AI consumption |
|
|
16
|
+
| `_aeo/generated-schemas.json` | JSON-LD schemas (Article, FAQPage, BreadcrumbList) |
|
|
17
|
+
| robots.txt suggestions | AI crawler allow/deny rules (printed, not auto-applied) |
|
|
18
|
+
|
|
19
|
+
## Workflow
|
|
20
|
+
|
|
21
|
+
1. **Identify build output.** Ask the user for the directory containing their built site (e.g., `dist/`, `out/`, `build/`). Check for common framework patterns:
|
|
22
|
+
- Next.js: `.next/` or `out/`
|
|
23
|
+
- Vite/Astro: `dist/`
|
|
24
|
+
- Hugo/Jekyll: `public/`
|
|
25
|
+
|
|
26
|
+
2. **Preview first.** Run:
|
|
27
|
+
```
|
|
28
|
+
npx aeoptimize generate <dir> --dry-run
|
|
29
|
+
```
|
|
30
|
+
Show the user what will be generated and explain each file's purpose.
|
|
31
|
+
|
|
32
|
+
3. **Confirm and generate.** On approval:
|
|
33
|
+
```
|
|
34
|
+
npx aeoptimize generate <dir>
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
4. **Review generated files.** Read each generated file and suggest manual refinements:
|
|
38
|
+
- `llms.txt`: Verify site name, description, and page listing are accurate
|
|
39
|
+
- JSON-LD: Check that generated schemas match the actual content
|
|
40
|
+
- robots.txt: Explain each AI crawler and let user decide allow/deny
|
|
41
|
+
|
|
42
|
+
5. **Integration guidance.** Explain how to deploy:
|
|
43
|
+
- Place `llms.txt` at site root (alongside `robots.txt`)
|
|
44
|
+
- Add `<link rel="llms-txt" href="/llms.txt">` to HTML `<head>`
|
|
45
|
+
- Inject generated JSON-LD into page `<head>` sections
|
|
46
|
+
- Merge robots.txt suggestions with existing rules
|
|
47
|
+
|
|
48
|
+
## Important
|
|
49
|
+
|
|
50
|
+
- Always preview with `--dry-run` before writing
|
|
51
|
+
- Never overwrite existing files without user confirmation
|
|
52
|
+
- Suggest running `/aeo-scan` first to understand current state
|
|
53
|
+
- The robots.txt suggestions are printed only — never auto-modify robots.txt
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: aeo-scan
|
|
3
|
+
description: Use when auditing a website or build output for AI search readiness, checking AI readability scores, or diagnosing why content isn't being cited by AI assistants like ChatGPT, Perplexity, or Google AI Overview
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# AEO Scan — AI Readability Audit
|
|
7
|
+
|
|
8
|
+
Scan a website or build directory and produce an interactive AI readability report.
|
|
9
|
+
|
|
10
|
+
## Scoring Dimensions (0-100)
|
|
11
|
+
|
|
12
|
+
| Dimension | Max | What it measures |
|
|
13
|
+
|-----------|-----|------------------|
|
|
14
|
+
| Structure | 25 | Heading hierarchy, paragraph length, FAQ presence, list usage |
|
|
15
|
+
| Citability | 25 | Self-contained statements, data/stats, definitions, attribution |
|
|
16
|
+
| Schema | 20 | JSON-LD presence, completeness, AI-relevant types |
|
|
17
|
+
| AI Metadata | 15 | llms.txt, robots.txt AI config, meta description |
|
|
18
|
+
| Content Density | 15 | Content vs boilerplate, keyword stuffing, uniqueness |
|
|
19
|
+
|
|
20
|
+
## Workflow
|
|
21
|
+
|
|
22
|
+
1. **Identify target.** Ask the user for a URL or directory path. If in a project with a build output (e.g., `dist/`, `out/`, `.next/`, `build/`), suggest scanning that.
|
|
23
|
+
|
|
24
|
+
2. **Run scan.** Execute:
|
|
25
|
+
```
|
|
26
|
+
npx aeoptimize scan <target> --json
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
3. **Present results.** Summarize the overall score and highlight:
|
|
30
|
+
- Dimensions scoring below 60% of their max
|
|
31
|
+
- All critical issues
|
|
32
|
+
- Top 3 high-impact suggestions
|
|
33
|
+
|
|
34
|
+
4. **Discuss improvements.** For each weak dimension, explain:
|
|
35
|
+
- Why it matters for AI search visibility
|
|
36
|
+
- Concrete steps to improve
|
|
37
|
+
- Expected score impact
|
|
38
|
+
|
|
39
|
+
5. **Offer next steps:**
|
|
40
|
+
- Score below 50? Suggest running `/aeo-transform` on the worst pages
|
|
41
|
+
- Missing llms.txt or schema? Suggest `/aeo-generate`
|
|
42
|
+
- Score above 80? Congratulate and suggest monitoring over time
|
|
43
|
+
|
|
44
|
+
## Important
|
|
45
|
+
|
|
46
|
+
- Always run the CLI with `--json` for machine-readable output
|
|
47
|
+
- Present scores visually with context, not just numbers
|
|
48
|
+
- Focus discussion on high-impact fixes first
|
|
49
|
+
- If scanning a URL fails (CORS, timeout), suggest scanning the local build output instead
|
|
50
|
+
- Version: 0.1.0
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: aeo-transform
|
|
3
|
+
description: Use when restructuring website content for better AI citation — splitting long paragraphs, adding FAQ schema, removing keyword stuffing, improving heading structure, or injecting structured data into HTML or Markdown files
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# AEO Transform — AI-Friendly Content Restructuring
|
|
7
|
+
|
|
8
|
+
Transform SEO-optimized content into AI-search-ready format using language understanding. This skill uses your Claude subscription — no additional API costs.
|
|
9
|
+
|
|
10
|
+
## Transformation Strategies
|
|
11
|
+
|
|
12
|
+
| Strategy | What it does | Impact |
|
|
13
|
+
|----------|-------------|--------|
|
|
14
|
+
| **Split paragraphs** | Break long paragraphs (>150 words) into self-contained statements | High |
|
|
15
|
+
| **Extract FAQ** | Find implicit Q&A content and convert to explicit FAQ with schema | High |
|
|
16
|
+
| **Remove keyword stuffing** | Replace repeated keywords with natural synonyms | High |
|
|
17
|
+
| **Improve headings** | Rewrite vague headings as specific, question-format headings | Medium |
|
|
18
|
+
| **Add structured data** | Inject JSON-LD based on content analysis | Medium |
|
|
19
|
+
| **Fix dangling references** | Rewrite paragraphs starting with "This", "It", "They" to be self-contained | Medium |
|
|
20
|
+
|
|
21
|
+
## Workflow
|
|
22
|
+
|
|
23
|
+
1. **Identify targets.** Ask the user which files to transform. If unsure, suggest running `/aeo-scan` first to find the lowest-scoring pages.
|
|
24
|
+
|
|
25
|
+
2. **Read and analyze.** For each file:
|
|
26
|
+
- Read the full content
|
|
27
|
+
- Run `npx aeoptimize scan <file> --json` to get the current score
|
|
28
|
+
- Identify which strategies apply based on the issues found
|
|
29
|
+
|
|
30
|
+
3. **Transform incrementally.** Apply one strategy at a time:
|
|
31
|
+
- Show the proposed change as a diff
|
|
32
|
+
- Explain why this change improves AI readability
|
|
33
|
+
- Wait for user approval before applying
|
|
34
|
+
- Move to the next strategy
|
|
35
|
+
|
|
36
|
+
4. **Preserve voice.** Critical rules:
|
|
37
|
+
- Never invent new content or add claims not in the original
|
|
38
|
+
- Preserve the author's writing style and tone
|
|
39
|
+
- Only restructure — do not rewrite meaning
|
|
40
|
+
- Keep all existing data, quotes, and references intact
|
|
41
|
+
|
|
42
|
+
5. **Verify improvement.** After all transforms:
|
|
43
|
+
- Re-run `npx aeoptimize scan <file> --json`
|
|
44
|
+
- Show before/after score comparison
|
|
45
|
+
- Highlight which dimensions improved
|
|
46
|
+
|
|
47
|
+
## Strategy Details
|
|
48
|
+
|
|
49
|
+
### Split Paragraphs
|
|
50
|
+
For each paragraph over 150 words:
|
|
51
|
+
- Identify distinct ideas within the paragraph
|
|
52
|
+
- Split at natural boundaries (topic shifts, "Additionally", "However")
|
|
53
|
+
- Ensure each new paragraph is self-contained (has its own subject, not just "It..." or "This...")
|
|
54
|
+
|
|
55
|
+
### Extract FAQ
|
|
56
|
+
Look for patterns like:
|
|
57
|
+
- Heading followed by a short answer paragraph
|
|
58
|
+
- "What is X?" / "How does X work?" patterns in body text
|
|
59
|
+
- Implicit questions answered in the content
|
|
60
|
+
|
|
61
|
+
Convert to:
|
|
62
|
+
- Explicit `<h3>` question headings
|
|
63
|
+
- Concise answer paragraphs
|
|
64
|
+
- FAQPage JSON-LD schema
|
|
65
|
+
|
|
66
|
+
### Remove Keyword Stuffing
|
|
67
|
+
When a word appears >3% of content (excluding stop words):
|
|
68
|
+
- Replace some occurrences with synonyms or related terms
|
|
69
|
+
- Remove redundant mentions that don't add meaning
|
|
70
|
+
- Ensure remaining usage feels natural
|
|
71
|
+
|
|
72
|
+
### Fix Dangling References
|
|
73
|
+
For paragraphs starting with pronouns/conjunctions:
|
|
74
|
+
- Replace "This feature" with "[Product name]'s feature"
|
|
75
|
+
- Replace "It provides" with "[Subject] provides"
|
|
76
|
+
- Replace "However," with a self-contained restatement
|
|
77
|
+
|
|
78
|
+
## Important
|
|
79
|
+
|
|
80
|
+
- Always show diffs before applying changes
|
|
81
|
+
- Transform one file at a time, one strategy at a time
|
|
82
|
+
- Score comparison before/after is mandatory
|
|
83
|
+
- This skill is interactive — never batch-transform without review
|
|
84
|
+
- Supports `.html` and `.md` files
|