Odel
geolint

geolint

Local
@iliasabk2TypeScriptMITUpdated Today

ESLint for AI search: audits AI-crawler access, llms.txt, schema and citability.

geolint logo

geolint

ESLint for AI search. Lint your website for AI-search readiness โ€” AI crawler access, llms.txt, structured data and citability.

npm version CI OpenSSF Scorecard MIT license node >= 22 npm downloads PRs welcome

๐Ÿ‡ฉ๐Ÿ‡ช Deutsch

geolint terminal demo

30-second quickstart

No install, no config:

npx @iliasabk/geolint check yoursite.com

geolint fetches the page, its robots.txt and llms.txt, evaluates 51 known AI crawler tokens against your robots.txt, runs 52 audit rules, and prints a scored report with a concrete fix for every finding.

Why

  • AI answers are the new front page. ChatGPT, Perplexity, Claude, Copilot and Google AI Overviews send traffic โ€” or don't โ€” based on whether their crawlers can fetch and quote your pages.
  • Most sites accidentally block or confuse AI crawlers. A stale Disallow: /, a noindex left over from staging, a client-rendered page that looks empty to a bot that doesn't run JavaScript.
  • Existing tools are blocklists or score-only web apps. They tell you to block everything, or give you a number with no path to improve it. geolint is the linter: concrete findings, concrete fixes, runnable in CI on every PR.

What it checks

52 rules across 5 categories โ€” geolint rules lists them all, and docs/rules.md documents what each rule checks, why it matters and how to fix violations.

CategoryRulesExamples
AI Crawler Access10ai-crawler/search-bots-blocked, ai-crawler/wildcard-block-all, ai-crawler/user-fetch-bypass, ai-crawler/stale-tokens
llms.txt10llms-txt/missing, llms-txt/invalid-structure, llms-txt/broken-links, llms-txt/relative-links
Structured Data6schema/no-jsonld, schema/invalid-jsonld, schema/missing-article-fields
Citability9content/thin-content, content/no-h1, content/missing-dates, content/no-question-headings
Technical Foundation10technical/client-rendered, technical/https, technical/slow-response, technical/sitemap-missing

What a report looks like

Real output, auditing the bundled demo site (examples/demo-site, which deliberately blocks two bots) โ€” trimmed for width:

$ geolint check localhost:4173 --ignore technical/https

  geolint v0.2.1 โ€” AI-search readiness
  http://localhost:4173/
  200 OK ยท text/html ยท TTFB 113ms ยท robots 200 ยท llms.txt 404

  โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘  86/100  Grade B

  CATEGORIES
    AI Crawler Access     โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘โ–‘   70  โœ— 2 errors
    llms.txt              โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘   92  โš  1 warning ยท 1 hint
    Structured Data       โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘   88  โš  1 warning ยท 3 hints
    Citability            โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘   82  โš  2 warnings ยท 3 hints
    Technical Foundation  โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ  100  โœ“ clean

  AI CRAWLER ACCESS โ€” 49/51 allowed ยท 2 blocked
    OpenAI
      GPTBot                        โœ“  training
      OAI-SearchBot                 โœ“  search
      ChatGPT-User                  โœ“  user-fetch
    Perplexity
      PerplexityBot                 โœ—  search
      Perplexity-User               โœ“  user-fetch
    Google
      Googlebot                     โœ“  search
      Google-Extended               โœ“  training
    โ€ฆ 51 tokens total, grouped by vendor โ€ฆ

  FINDINGS
    AI Crawler Access
      โœ— ai-crawler/search-bots-blocked  PerplexityBot is blocked by robots.txt โ€” Perplexity cannot use your pages as AI answer sources
          fix: Remove the Disallow covering PerplexityBot in robots.txt, or add an explicit "Allow: /" for it.
          evidence: Disallow: / (matched by PerplexityBot)
    llms.txt
      โš  llms-txt/missing                No llms.txt found
          fix: Create /llms.txt at the site root: an H1 title, a short blockquote summary, and ## sections linking to your key content.
          evidence: http://localhost:4173/llms.txt โ†’ HTTP 404

  โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
  2 errors ยท 4 warnings ยท 7 hints ยท 32/44 checks passed

Every finding carries a rule id, a severity, the evidence geolint matched, and a fix. Compare two pages or two competitors head-to-head:

geolint check a.com --compare b.com

Commands

CommandWhat it doesKey flags
geolint check <url>Audit a single URL--format, --fail-under, --only/--ignore/--category, --compare, --baseline, --badge, --verbose
geolint crawl <url>Crawl same-origin pages and audit the whole site--max-pages, --max-depth, --concurrency, --fail-under
geolint init <url>Crawl the site and generate a llms.txt-o, --max-pages
geolint diff <old.json> <new.json>Compare two JSON reports: score delta, added/resolved findingsโ€”
geolint rulesList the 52 audit rules--category, --format table|json|markdown
geolint botsList the 51 known AI crawlers and the impact of blocking each--format table|json
geolint mcpRun an MCP server on stdio for AI assistants--timeout

Full flag reference: docs/configuration.md.

Run it in CI

GitHub Action

- uses: iliasabk/geolint@v1
  id: geolint
  with:
    url: https://example.com
    fail-under: 80

- uses: github/codeql-action/upload-sarif@v3
  if: always()
  with:
    sarif_file: ${{ steps.geolint.outputs.sarif-file }}

The action produces score/grade step outputs, a SARIF report for GitHub code scanning, and a markdown report for job summaries and PR comments. Full recipes โ€” SARIF upload, updating a single PR comment, baseline drift detection โ€” in docs/github-action.md.

Any other CI

npx @iliasabk/geolint check https://example.com --fail-under 80

Exit code is 1 when the score drops below the gate (or findings regress against --baseline), 0 otherwise โ€” works in GitLab CI, CircleCI, npm scripts, pre-deploy hooks.

Show your score as a README badge

npx @iliasabk/geolint check https://example.com --badge
# โ†’ writes geolint-badge.svg + prints the markdown snippet to paste

Commit the SVG, or regenerate a shields endpoint JSON in CI (--badge-endpoint) for a badge that never goes stale.

Output formats

-f pretty (default) renders the terminal report above. The machine formats:

  • -f json โ€” the full ScanReport: findings, per-category scores, bot access matrix
  • -f sarif โ€” SARIF 2.1.0, upload straight to GitHub code scanning
  • -f markdown โ€” PR-comment/job-summary-ready tables
  • -f html โ€” a self-contained interactive report (score ring, findings filter, bot matrix) you can share or host anywhere

Add -o report.json to write to a file; stdout stays clean for piping.

geolint on the real web

The repo dogfoods itself: a nightly workflow re-audits eight well-known sites and commits the scores back, and the showcase site publishes the full interactive reports โ€” github.com, anthropic.com, stripe.com and more, regenerated on every push to main.

Programmatic API

import { scan } from '@iliasabk/geolint';

const report = await scan('https://example.com', {
  ignore: ['technical/https'],
  timeout: 10_000,
});

console.log(report.score, report.grade);          // e.g. 86 'B'
for (const f of report.findings) {
  console.log(f.severity, f.ruleId, f.message, f.fix);
}

scan(url, options) returns a typed ScanReport. Also exported: the bot registry (AI_BOTS, botsByPurpose), the rule registry (allRules, ruleById), robots.txt/llms.txt parsers, badge generators, scorers and all four reporters.

Use it from AI assistants (MCP)

geolint mcp speaks the Model Context Protocol over stdio โ€” Claude Desktop, Cursor, VS Code and Windsurf can audit sites, generate llms.txt and compare URLs as native tools:

// claude_desktop_config.json / ~/.cursor/mcp.json
{
  "mcpServers": {
    "geolint": {
      "command": "npx",
      "args": ["-y", "@iliasabk/geolint", "mcp"]
    }
  }
}

Five tools: audit_url, generate_llms_txt, compare_urls, list_rules, list_ai_bots โ€” all read-only, with structured output and per-call timeouts. Setup for every client: docs/mcp.md.

The bot registry is the point

geolint bots lists 51 AI crawler tokens with a purpose-aware impact assessment โ€” because "should I block this bot?" has a different answer for each:

PurposeExamplesIf you block it
trainingGPTBot, ClaudeBot, CCBotabsent from future training data
searchOAI-SearchBot, PerplexityBot, Claude-SearchBotinvisible in AI answers now
user-fetchChatGPT-User, Claude-Userinvisible in AI answers now
mixedBytespider, Amazonbot, Diffbotboth

And two nuances other tools miss:

  • Some fetchers ignore robots.txt. OpenAI, Perplexity and Meta document that their user-triggered fetchers (ChatGPT-User, Perplexity-User, Meta-ExternalFetcher) may not honor robots.txt. ai-crawler/user-fetch-bypass tells you when a Disallow won't work โ€” enforce at the WAF/auth layer instead.
  • Stale tokens. anthropic-ai, Claude-Web, FacebookBot are retired. ai-crawler/stale-tokens flags them and names the replacement token โ€” a User-agent: anthropic-ai rule does nothing today.

Control-only tokens like Google-Extended and Applebot-Extended never fetch at all โ€” they only set a preference โ€” and geolint treats them accordingly.

What geolint is honest about

  • llms.txt is a proposal, not a standard. No major AI vendor has committed to reading it โ€” so llms-txt/* findings are weighted as warnings and hints, not errors. geolint still checks it (and geolint init generates it) because adoption is growing and the cost is one file.
  • Correlation โ‰  causation. The citability rules are grounded in published GEO research (quotations/statistics/citations measurably lift share-of-answer; AI crawlers other than Googlebot and Applebot don't execute JavaScript), but signals like question-shaped headings are hints, not facts โ€” they're info severity and geolint says so.
  • Every rule shows its reasoning. docs/rules.md documents why each rule exists; the research sources are in docs/research-notes.md, including the vendor docs behind every bot's robots.txt posture.

Compared to the alternatives

Purpose-aware bot registryPer-vendor robots.txt postureRuns in CIFix per findingGenerates llms.txtFree / OSS
geolintโœ…โœ…โœ…โœ…โœ…โœ…
ai.robots.txt-style blocklistsโŒโŒn/aโŒโŒโœ…
GEO-optimizer skills / prompt packsโŒโŒโŒโŒโŒvaries
llms.txt validatorsโŒโŒsomepartialsomeโœ…
Hosted GEO audit web appspartialโŒโŒpartialโŒโŒ

Details and the reasoning behind each column: docs/comparison.md. geolint also ships an MCP server, a score badge and regression baselines.

Roadmap

Planned for v0.4+:

  • geolint watch โ€” re-audit on deploys/file changes
  • Custom rule API for project-specific checks
  • Deeper schema coverage (more @type validators)
  • Homebrew formula
  • Report localization beyond English

Contributing

Issues and PRs welcome โ€” see CONTRIBUTING.md. New rules are the best contribution: each needs a check(ctx), findings with fix, a test and a docs entry.

License

MIT ยท changelog ยท security


If geolint helped, a โญ helps others find it.