{"id":50901698,"url":"https://github.com/didrod205/aeolint","last_synced_at":"2026-06-16T03:04:42.440Z","repository":{"id":361582179,"uuid":"1255015025","full_name":"didrod205/aeolint","owner":"didrod205","description":"Local CLI that lints HTML/Markdown for SEO and answer-engine (AEO/GEO) readiness — structured data, content quality, performance and crawlability. Deterministic, no API key, no server.","archived":false,"fork":false,"pushed_at":"2026-05-31T09:43:30.000Z","size":65,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-31T11:15:15.905Z","etag":null,"topics":["aeo","ai-search","answer-engine-optimization","cli","content-audit","generative-engine-optimization","geo","json-ld","lighthouse-alternative","schema-org","seo","seo-audit","seo-cli","structured-data","typescript"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/@didrod2539/aeolint","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/didrod205.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"custom":["https://elab-studio.lemonsqueezy.com/checkout/buy/5d059b89-51d0-456b-b33a-ed56994f7010"]}},"created_at":"2026-05-31T09:40:23.000Z","updated_at":"2026-05-31T09:45:03.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/didrod205/aeolint","commit_stats":null,"previous_names":["didrod205/aeolint"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/didrod205/aeolint","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/didrod205%2Faeolint","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/didrod205%2Faeolint/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/didrod205%2Faeolint/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/didrod205%2Faeolint/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/didrod205","download_url":"https://codeload.github.com/didrod205/aeolint/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/didrod205%2Faeolint/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34388683,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-16T02:00:06.860Z","response_time":126,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aeo","ai-search","answer-engine-optimization","cli","content-audit","generative-engine-optimization","geo","json-ld","lighthouse-alternative","schema-org","seo","seo-audit","seo-cli","structured-data","typescript"],"created_at":"2026-06-16T03:04:42.371Z","updated_at":"2026-06-16T03:04:42.427Z","avatar_url":"https://github.com/didrod205.png","language":"TypeScript","funding_links":["https://elab-studio.lemonsqueezy.com/checkout/buy/5d059b89-51d0-456b-b33a-ed56994f7010"],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\n# 🔎 aeolint\n\n### Lint your pages for SEO **and** answer-engine (AEO/GEO) readiness — locally, no API key.\n\n[![npm version](https://img.shields.io/npm/v/@didrod2539/aeolint.svg?color=success)](https://www.npmjs.com/package/@didrod2539/aeolint)\n[![CI](https://github.com/didrod205/aeolint/actions/workflows/ci.yml/badge.svg)](https://github.com/didrod205/aeolint/actions/workflows/ci.yml)\n[![node](https://img.shields.io/node/v/@didrod2539/aeolint.svg)](https://www.npmjs.com/package/@didrod2539/aeolint)\n[![license](https://img.shields.io/npm/l/@didrod2539/aeolint.svg)](./LICENSE)\n\nA deterministic command-line auditor that scores your HTML/Markdown for **SEO**,\n**Answer Engine Optimization (AEO)**, **Generative Engine Optimization (GEO)**,\nstructured data, content quality, headings, performance risk and crawlability —\nthen tells you exactly what to fix.\n\n\u003c/div\u003e\n\n---\n\nSearch is splitting in two. Half your traffic still comes from the classic\nten blue links; the other half is being quietly answered **inside** ChatGPT,\nPerplexity and Google's AI Overviews — and those answer engines pick what to\nquote based on signals most \"SEO checkers\" never look at: question-style\nheadings, a concise lead answer, `FAQPage`/`Article` JSON-LD, self-contained\nparagraphs, clean crawlability.\n\n**aeolint** audits all of it from one command. It's **deterministic** (same page\n→ same score, every time — perfect for CI gates), runs **100% locally** with\n**no API key and no server**, and ships **40+ checks** across 7 categories with\nan A–F grade and concrete fixes.\n\n```bash\nnpx @didrod2539/aeolint scan ./public\n```\n\n\u003e 📸 _Screenshot / demo GIF placeholder:_ `./docs/screenshot.png` — record the\n\u003e terminal running `npx @didrod2539/aeolint scan examples/good.html examples/bad.html`.\n\n## Why it exists\n\n- **AEO/GEO is the new frontier, and existing tools ignore it.** Lighthouse and\n  most \"SEO linters\" check titles and meta tags but say nothing about whether an\n  AI can extract and cite your content. aeolint scores that explicitly.\n- **AI can't reliably replace this.** \"Is my page answer-engine ready?\" sounds\n  like an LLM question, but you want a **repeatable, auditable** answer you can\n  gate a deploy on — not a different vibe each time you ask. aeolint is a fixed\n  rule engine: deterministic, versioned, diff-able.\n- **No server, no key, no data leaving your machine.** Point it at local files,\n  a folder, or a URL. Nothing is uploaded. It works offline.\n- **Built for CI.** `--min-score 80` exits non-zero so a regression fails the\n  build, just like a test.\n\n## Who it's for\n\n**Developers** (gate SEO/AEO in CI), **content \u0026 marketing** (make posts\nquotable by AI search), **technical SEOs** (a scriptable, deterministic audit),\n**docs teams**, and **indie makers** who want their pages cited by ChatGPT and\nPerplexity — not just ranked.\n\n## Install\n\nRun it without installing:\n\n```bash\nnpx @didrod2539/aeolint scan ./public\n```\n\nOr install globally / per-project:\n\n```bash\nnpm install -g @didrod2539/aeolint   # global CLI (provides the `aeolint` command)\nnpm install -D @didrod2539/aeolint   # project dev-dependency (for CI)\n```\n\nNode ≥ 18. Ships ESM + CJS + TypeScript types. Zero network calls unless you\nscan a URL.\n\n## Usage\n\n```bash\n# Scan files, a whole directory, or a live URL\naeolint scan examples/good.html\naeolint scan ./public                       # recurses for .html/.htm/.md\naeolint scan https://example.com            # also reads robots.txt + sitemap.xml\n\n# Export machine-readable + shareable reports\naeolint scan ./public --json report.json --md report.md\n\n# Fail CI when quality regresses\naeolint scan ./public --min-score 80\n\n# Use a config file (auto-detected, or pass one)\naeolint scan ./public --config aeolint.config.json\n\n# Re-render a saved JSON report as Markdown\naeolint report report.json --md report.md\n\n# Scaffold a config with all defaults\naeolint init\n\n# Built-ins\naeolint --help\naeolint --version\n```\n\n### Example output\n\n```\nHow to Brew Pour-Over Coffee: A Beginner's Guide\n  95/100 (A)  examples/good.html\n  SEO basics                         100\n  Answer-engine readiness (AEO/GEO)   88\n  Structured data                    100\n  Content quality                     83\n  Headings \u0026 structure               100\n  Performance risks                  100\n  Crawlability                       100\n\n  ⚠ The opening paragraph is long\n      → Lead with a ≤320-char direct answer, then expand.\n\nOverall  95/100 (A)  1 page(s), 0 error(s), 1 warning(s)\n```\n\nSee [`examples/sample-report.md`](./examples/sample-report.md) and\n[`examples/sample-report.json`](./examples/sample-report.json) for full reports.\n\n### Three real scenarios\n\n1. **CI gate for a blog/docs site** — add\n   `aeolint scan ./dist --min-score 85` to your build. A PR that ships a page\n   with a missing `\u003ctitle\u003e` or broken JSON-LD fails before it merges.\n2. **Make an article quotable by AI search** — run `aeolint scan post.html`,\n   then fix the AEO findings (add a lead answer, question headings, a short FAQ,\n   `FAQPage` JSON-LD) until the **Answer-engine** category hits 100.\n3. **Audit a competitor or a live page** — `aeolint scan https://example.com\n   --md audit.md` produces a shareable Markdown report including their\n   robots.txt / sitemap signals.\n\n## What it checks\n\n| Category | Examples of checks |\n| --- | --- |\n| **SEO basics** | title length, meta description, `lang`, viewport, charset, canonical, Open Graph |\n| **Answer-engine (AEO/GEO)** | question-style headings, concise lead answer, FAQ section, TL;DR/summary, lists \u0026 tables, substance |\n| **Structured data** | JSON-LD present \u0026 valid, `@type` detection, and **Google rich-result eligibility** — per-type required/recommended fields for Article, Product, FAQPage, Recipe, Event, JobPosting, Breadcrumb, Organization, LocalBusiness, Review, Video, HowTo, WebSite sitelinks \u0026 more |\n| **Content quality** | word depth, Flesch readability, image `alt` coverage, descriptive link text |\n| **Headings** | single `\u003ch1\u003e`, no skipped levels, no empty headings |\n| **Performance risk** | render-blocking scripts, image dimensions (CLS), lazy-loading, base64 bloat, DOM size |\n| **Crawlability** | `noindex`, canonical, `robots.txt` `Sitemap:` directive \u0026 blocks, sitemap entries |\n\nScoring is transparent: each check is a weighted *pass / warning / error*, rolled\nup per category and combined into a config-weighted overall score and A–F grade.\n\n### Rich-result eligibility (the deep structured-data check)\n\nA page can have perfectly valid JSON-LD and still **win no rich result** — because\nGoogle only renders review stars, FAQ accordions, recipe cards, job postings, etc.\nwhen the *required* properties for that feature are present. Those requirements are\nan exact, documented spec, so aeolint checks them deterministically — no\ncopy-pasting each URL into the Rich Results Test:\n\n```\n✗ Product snippet / Merchant listing (Product) is NOT eligible for a rich result\n    Missing required: one of: offers, review, aggregateRating\n    → Add the required property so Google can render the Product rich result.\n\n⚠ Article (Article) is eligible, but missing recommended fields\n    Recommended: image, dateModified\n\n✓ FAQ (FAQPage) is eligible for a rich result\n```\n\nThis is the check that catches a refactor silently dropping `offers` from a product\npage — the kind of regression that quietly tanks click-through. Put\n`aeolint scan ./dist --min-score 85` in CI and the build fails the moment a page\nloses its rich-result eligibility. Covered features: **Article, Product, FAQPage,\nRecipe, Event, JobPosting, BreadcrumbList, Organization/Logo, LocalBusiness, Review,\nVideoObject, HowTo, WebSite (sitelinks searchbox), SoftwareApplication.**\n\n## Configuration\n\nCreate `aeolint.config.json` (or run `aeolint init`):\n\n```json\n{\n  \"minScore\": 80,\n  \"disableCategories\": [],\n  \"ignore\": [\"meta.og\"],\n  \"thresholds\": { \"minWords\": 250, \"descriptionMax\": 160 },\n  \"categoryWeights\": { \"aeo\": 1.5, \"structured-data\": 1.2 }\n}\n```\n\n- `minScore` — CI gate threshold (overridable with `--min-score`).\n- `disableCategories` — skip whole categories, e.g. `[\"performance\"]`.\n- `ignore` — silence individual checks by id (shown in reports, e.g. `meta.og`).\n- `thresholds` — tune lengths, word counts, DOM/script limits.\n- `categoryWeights` — re-weight the overall score for your priorities.\n\n## Programmatic API\n\n```ts\nimport { auditPage, buildReport, toMarkdown } from \"@didrod2539/aeolint\";\n\nconst page = auditPage({ source: \"post.html\", html });\nconsole.log(page.score, page.grade, page.findings);\n\nconst report = buildReport([page], \"0.1.0\");\nawait fs.writeFile(\"report.md\", toMarkdown(report));\n```\n\n## FAQ\n\n**Does it call an AI / send my pages anywhere?**\nNo. aeolint is a deterministic rule engine that runs entirely on your machine.\nNo API key, no telemetry, no uploads. Scanning a URL is the only time it touches\nthe network — to fetch that page and its `robots.txt`/`sitemap.xml`.\n\n**Is \"AEO score\" a real Google ranking?**\nNo. There is no official answer-engine score. aeolint encodes widely-recommended,\ncheckable best practices (concise answers, question headings, schema.org, clean\ncrawlability) into a transparent, weighted score so you can track and gate it.\nTreat it as a lint, not a guarantee of placement.\n\n**How is this different from Lighthouse?**\nLighthouse audits performance/accessibility/SEO of a rendered page in a browser.\naeolint is a static, dependency-light CLI focused on **content \u0026 answer-engine\nreadiness** (AEO/GEO, structured data, quotability) that you can run on raw HTML\nor Markdown in CI without a headless browser.\n\n**Can it scan a whole site?**\nPoint it at a directory of built HTML (`aeolint scan ./dist`) and it recurses.\nMulti-URL crawling of live sites is on the roadmap — PRs welcome.\n\n**Why are my passing checks not shown?**\nThe console view highlights problems; passes count toward the score. Use\n`--json` / `--md` for the full breakdown including passes per category.\n\n## Contributing\n\nContributions welcome! New checks are small, self-contained modules in\n`src/audits/`. See [CONTRIBUTING.md](./CONTRIBUTING.md) and the\n[Code of Conduct](./CODE_OF_CONDUCT.md).\n\n```bash\ngit clone https://github.com/didrod205/aeolint.git\ncd aeolint\nnpm install\nnpm test            # run the suite\nnpm run build       # build the CLI + library\nnode dist/cli.js scan examples/good.html\n```\n\n## 💖 Sponsor\n\naeolint is free, MIT-licensed, and built in spare time. If it saved you a manual\naudit (or a debugging session), please consider supporting it:\n\n- ⭐ **Star this repo** — free, and it genuinely helps others find it.\n- 🍋 **[Sponsor via Lemon Squeezy](https://elab-studio.lemonsqueezy.com/checkout/buy/5d059b89-51d0-456b-b33a-ed56994f7010)** — one-time or recurring support.\n\n**Where your support goes:** more checks (Product/HowTo/BreadcrumbList schema,\nhreflang, Twitter cards, internal-link analysis), multi-URL site crawling, an\nHTML report format, framework presets (Next.js/Astro/Hugo), and fast issue\nresponses.\n\n## License\n\n[MIT](./LICENSE) © aeolint contributors\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdidrod205%2Faeolint","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdidrod205%2Faeolint","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdidrod205%2Faeolint/lists"}