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.
@@ -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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -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