ranq 0.3.0__tar.gz

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.
Files changed (118) hide show
  1. ranq-0.3.0/.gitignore +50 -0
  2. ranq-0.3.0/CHANGELOG.md +101 -0
  3. ranq-0.3.0/LICENSE +21 -0
  4. ranq-0.3.0/PKG-INFO +322 -0
  5. ranq-0.3.0/README.md +271 -0
  6. ranq-0.3.0/pyproject.toml +102 -0
  7. ranq-0.3.0/src/ranq/__init__.py +4 -0
  8. ranq-0.3.0/src/ranq/analysis/__init__.py +0 -0
  9. ranq-0.3.0/src/ranq/analysis/aeo.py +250 -0
  10. ranq-0.3.0/src/ranq/analysis/competitors.py +88 -0
  11. ranq-0.3.0/src/ranq/analysis/framework.py +62 -0
  12. ranq-0.3.0/src/ranq/analysis/keywords.py +539 -0
  13. ranq-0.3.0/src/ranq/analysis/pagetype.py +188 -0
  14. ranq-0.3.0/src/ranq/analysis/performance.py +154 -0
  15. ranq-0.3.0/src/ranq/analysis/structured_data.py +258 -0
  16. ranq-0.3.0/src/ranq/analysis/suggest.py +69 -0
  17. ranq-0.3.0/src/ranq/audit.py +89 -0
  18. ranq-0.3.0/src/ranq/checks/__init__.py +12 -0
  19. ranq-0.3.0/src/ranq/checks/aeo.py +179 -0
  20. ranq-0.3.0/src/ranq/checks/base.py +124 -0
  21. ranq-0.3.0/src/ranq/checks/crawl.py +385 -0
  22. ranq-0.3.0/src/ranq/checks/geo.py +285 -0
  23. ranq-0.3.0/src/ranq/checks/keywords.py +34 -0
  24. ranq-0.3.0/src/ranq/checks/onpage.py +431 -0
  25. ranq-0.3.0/src/ranq/checks/performance.py +114 -0
  26. ranq-0.3.0/src/ranq/checks/structured_data.py +239 -0
  27. ranq-0.3.0/src/ranq/checks/technical.py +757 -0
  28. ranq-0.3.0/src/ranq/cli.py +1208 -0
  29. ranq-0.3.0/src/ranq/config.py +170 -0
  30. ranq-0.3.0/src/ranq/crawler/__init__.py +0 -0
  31. ranq-0.3.0/src/ranq/crawler/crawler.py +305 -0
  32. ranq-0.3.0/src/ranq/crawler/parser.py +261 -0
  33. ranq-0.3.0/src/ranq/crawler/renderer.py +52 -0
  34. ranq-0.3.0/src/ranq/crawler/sitemaps.py +131 -0
  35. ranq-0.3.0/src/ranq/enrichers.py +67 -0
  36. ranq-0.3.0/src/ranq/fix/__init__.py +0 -0
  37. ranq-0.3.0/src/ranq/fix/bundle.py +199 -0
  38. ranq-0.3.0/src/ranq/generators/__init__.py +0 -0
  39. ranq-0.3.0/src/ranq/generators/framework.py +277 -0
  40. ranq-0.3.0/src/ranq/generators/headers.py +140 -0
  41. ranq-0.3.0/src/ranq/generators/llms.py +150 -0
  42. ranq-0.3.0/src/ranq/generators/markdown.py +216 -0
  43. ranq-0.3.0/src/ranq/generators/meta.py +131 -0
  44. ranq-0.3.0/src/ranq/generators/redirects.py +233 -0
  45. ranq-0.3.0/src/ranq/generators/robots.py +61 -0
  46. ranq-0.3.0/src/ranq/generators/schema.py +469 -0
  47. ranq-0.3.0/src/ranq/generators/sitemap.py +80 -0
  48. ranq-0.3.0/src/ranq/geo/__init__.py +0 -0
  49. ranq-0.3.0/src/ranq/geo/ai_bots.py +202 -0
  50. ranq-0.3.0/src/ranq/geo/analysis.py +79 -0
  51. ranq-0.3.0/src/ranq/geo/citation.py +125 -0
  52. ranq-0.3.0/src/ranq/geo/commoncrawl.py +61 -0
  53. ranq-0.3.0/src/ranq/geo/entity.py +363 -0
  54. ranq-0.3.0/src/ranq/geo/extras.py +85 -0
  55. ranq-0.3.0/src/ranq/geo/visibility.py +230 -0
  56. ranq-0.3.0/src/ranq/http.py +202 -0
  57. ranq-0.3.0/src/ranq/indexing/__init__.py +0 -0
  58. ranq-0.3.0/src/ranq/indexing/bing.py +61 -0
  59. ranq-0.3.0/src/ranq/indexing/diagnose.py +231 -0
  60. ranq-0.3.0/src/ranq/indexing/engines.py +85 -0
  61. ranq-0.3.0/src/ranq/indexing/gsc.py +177 -0
  62. ranq-0.3.0/src/ranq/indexing/indexnow.py +118 -0
  63. ranq-0.3.0/src/ranq/models.py +275 -0
  64. ranq-0.3.0/src/ranq/offpage/__init__.py +0 -0
  65. ranq-0.3.0/src/ranq/offpage/broken_links.py +85 -0
  66. ranq-0.3.0/src/ranq/offpage/checklist.py +129 -0
  67. ranq-0.3.0/src/ranq/offpage/mentions.py +140 -0
  68. ranq-0.3.0/src/ranq/py.typed +0 -0
  69. ranq-0.3.0/src/ranq/report/__init__.py +0 -0
  70. ranq-0.3.0/src/ranq/report/builder.py +223 -0
  71. ranq-0.3.0/src/ranq/report/compare.py +113 -0
  72. ranq-0.3.0/src/ranq/report/limitations.py +49 -0
  73. ranq-0.3.0/src/ranq/report/render.py +71 -0
  74. ranq-0.3.0/src/ranq/report/templates/report.html.j2 +273 -0
  75. ranq-0.3.0/src/ranq/report/templates/report.md.j2 +177 -0
  76. ranq-0.3.0/src/ranq/robots.py +189 -0
  77. ranq-0.3.0/src/ranq/sources.py +47 -0
  78. ranq-0.3.0/src/ranq/storage.py +210 -0
  79. ranq-0.3.0/src/ranq/urls.py +81 -0
  80. ranq-0.3.0/src/ranq/wizard.py +121 -0
  81. ranq-0.3.0/tests/conftest.py +42 -0
  82. ranq-0.3.0/tests/plugins/demo_plugin.py +21 -0
  83. ranq-0.3.0/tests/serve_site.py +120 -0
  84. ranq-0.3.0/tests/site/about.html +20 -0
  85. ranq-0.3.0/tests/site/blog/index.html +17 -0
  86. ranq-0.3.0/tests/site/blog/what-is-widget-testing.html +35 -0
  87. ranq-0.3.0/tests/site/contact.html +16 -0
  88. ranq-0.3.0/tests/site/dup-a.html +15 -0
  89. ranq-0.3.0/tests/site/dup-b.html +15 -0
  90. ranq-0.3.0/tests/site/index.html +32 -0
  91. ranq-0.3.0/tests/site/js-app.html +7 -0
  92. ranq-0.3.0/tests/site/missing-title.html +5 -0
  93. ranq-0.3.0/tests/site/noindex-page.html +10 -0
  94. ranq-0.3.0/tests/site/orphan.html +11 -0
  95. ranq-0.3.0/tests/site/products/widget.html +20 -0
  96. ranq-0.3.0/tests/site/resources.html +14 -0
  97. ranq-0.3.0/tests/site/robots.txt +7 -0
  98. ranq-0.3.0/tests/site/sitemap.xml +10 -0
  99. ranq-0.3.0/tests/test_analysis_network.py +94 -0
  100. ranq-0.3.0/tests/test_audit_integration.py +143 -0
  101. ranq-0.3.0/tests/test_compare.py +76 -0
  102. ranq-0.3.0/tests/test_fix_integration.py +106 -0
  103. ranq-0.3.0/tests/test_geo.py +196 -0
  104. ranq-0.3.0/tests/test_indexing_offpage.py +294 -0
  105. ranq-0.3.0/tests/test_plugins_and_report.py +60 -0
  106. ranq-0.3.0/tests/test_wizard.py +53 -0
  107. ranq-0.3.0/tests/unit/test_aeo_generators.py +105 -0
  108. ranq-0.3.0/tests/unit/test_config_scope.py +40 -0
  109. ranq-0.3.0/tests/unit/test_framework.py +45 -0
  110. ranq-0.3.0/tests/unit/test_headers.py +36 -0
  111. ranq-0.3.0/tests/unit/test_keywords.py +61 -0
  112. ranq-0.3.0/tests/unit/test_markdown.py +31 -0
  113. ranq-0.3.0/tests/unit/test_parsing.py +137 -0
  114. ranq-0.3.0/tests/unit/test_report_safety.py +85 -0
  115. ranq-0.3.0/tests/unit/test_robots.py +61 -0
  116. ranq-0.3.0/tests/unit/test_schema_merge.py +91 -0
  117. ranq-0.3.0/tests/unit/test_structured_data.py +111 -0
  118. ranq-0.3.0/tests/unit/test_unreachable.py +150 -0
ranq-0.3.0/.gitignore ADDED
@@ -0,0 +1,50 @@
1
+ # Python
2
+ .venv/
3
+ venv/
4
+ __pycache__/
5
+ *.py[cod]
6
+ *.egg-info/
7
+ dist/
8
+ build/
9
+
10
+ # Tooling caches
11
+ .pytest_cache/
12
+ .mypy_cache/
13
+ .ruff_cache/
14
+ .coverage
15
+ htmlcov/
16
+
17
+ # Ranq local data and outputs
18
+ # Your own site config (brand facts, competitors) - keep it local
19
+ /ranq.yaml
20
+ /ranq.yml
21
+ /.ranq.yaml
22
+ .ranq/
23
+ ranq-report/
24
+ ranq-fixes/
25
+ # Names from before the rename to Ranq
26
+ /seoforge.yaml
27
+ /seoforge.yml
28
+ /.seoforge.yaml
29
+ .seoforge/
30
+ seoforge-report/
31
+ seoforge-fixes/
32
+ *.sqlite
33
+ *.db
34
+
35
+ # Secrets and credentials (keys belong in environment variables)
36
+ .env
37
+ .env.*
38
+ *.pem
39
+ gsc-service-account*.json
40
+ *service-account*.json
41
+ credentials*.json
42
+ # Stray downloads such as Google Cloud key files in the repo root
43
+ /*.json
44
+
45
+ # Editors / OS
46
+ .idea/
47
+ .vscode/
48
+ *.swp
49
+ .DS_Store
50
+ Thumbs.db
@@ -0,0 +1,101 @@
1
+ # Changelog
2
+
3
+ All notable changes to SEOForge. The format follows
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project uses
5
+ [Semantic Versioning](https://semver.org/).
6
+
7
+ ## [0.3.0] - 2026-10-12
8
+
9
+ ### Changed
10
+
11
+ - **Renamed from SEOForge to Ranq.** The package, the command (`ranq audit ...`), the config
12
+ file (`ranq.yaml`), the output folders (`ranq-report/`, `ranq-fixes/`), the data folder
13
+ (`.ranq/`), the crawler User-Agent (`Ranq/<version>`, robots.txt group `Ranq`) and the
14
+ plugin entry-point group (`ranq.checks`) all use the new name. An existing `seoforge.yaml`
15
+ is still read when there is no `ranq.yaml`. The GitHub repository moved to
16
+ `Dreadonyx/ranq`; the old URL redirects.
17
+ - Built and maintained by [NodeKraft](https://nodekraft.tech): credited in the README, the
18
+ package metadata and the footer of every report.
19
+
20
+ ### Security
21
+
22
+ - Report links built from crawled data (such as `sameAs` URLs) only allow `http(s)`; a
23
+ `javascript:` URL could otherwise run in the HTML report when clicked.
24
+ - HTML comments in generated meta tags cannot be closed early by crawled text.
25
+ - CI scans the full history for secrets (gitleaks) and audits dependencies (pip-audit);
26
+ workflow checkouts no longer keep the GitHub token; the PyPI publish action is pinned to a
27
+ commit; Dependabot keeps actions and dependencies updated; `SECURITY.md` explains private
28
+ reporting.
29
+ - `jinja2` minimum raised to 3.1.6.
30
+
31
+ ## [0.2.0] - 2026-10-12
32
+
33
+ ### Added
34
+
35
+ - **Per-page scores**: every crawled page gets a score from its own issues, shown in the CLI,
36
+ a Pages tab in the HTML report, the Markdown report and `report.json`.
37
+ - **Audit comparison**: each audit is compared with the previous report for the same site (or
38
+ `--compare old.json`): score change, fixed / new / changed issues and page-score changes.
39
+ Every run is archived in `<out>/history/`. `--no-compare` turns it off.
40
+ - **Guided `seoforge init`**: asks a few questions pre-filled from what the home page already
41
+ says (brand name, description, city, founding year, social profiles). `--example` writes
42
+ the commented template.
43
+ - **Framework detection** (Next.js App/Pages Router, Nuxt, Astro, SvelteKit, Remix, Gatsby,
44
+ Docusaurus, WordPress, Shopify, Wix, Squarespace, Webflow, Framer, Ghost, Hugo, Jekyll,
45
+ Eleventy). `fix` writes `FRAMEWORK.md` saying where each file goes; Next.js App Router sites
46
+ also get `app/robots.ts`, `app/sitemap.ts` and a safe JSON-LD component.
47
+ - New checks:
48
+ - `no-custom-domain` (high): the site runs on a platform subdomain (about 80 hosts,
49
+ including Vercel, Netlify, Hostinger, GitHub Pages, Cloudflare Pages, Wix and Webflow).
50
+ - `site-unreachable` (critical): no crawlable HTML page, with the reason (HTTP status,
51
+ fetch error, robots.txt, off-site redirect).
52
+ - `h1-hidden-in-html` (medium): the H1 is served invisible and revealed by JavaScript,
53
+ which delays Largest Contentful Paint.
54
+ - `og-url-mismatch` (medium): og:url points to another page.
55
+ - `jsonld-webpage-wrong-url` (medium): a WebPage node describes another URL (layout-level
56
+ markup copied to every page).
57
+ - `brand-description-inconsistent` (low): JSON-LD, llms.txt and config describe the brand
58
+ differently.
59
+ - `email-obfuscated` (low): Cloudflare Email Obfuscation hides the contact address.
60
+
61
+ ### Changed
62
+
63
+ - **Health score**: site-wide issues (missing About page, llms.txt, robots.txt, Organization
64
+ markup, HTTPS...) now count in full instead of being diluted by page count. A site where
65
+ nothing could be crawled scores 0. Penalties map to the score on a diminishing curve
66
+ (`100 * e^(-penalty/100)`), so sites with many problems stay comparable instead of all
67
+ bottoming out at 0.
68
+ - `fix` builds JSON-LD on top of the site's existing markup and writes one `@graph` for the
69
+ shared layout plus one per page, so each file can replace what is there without losing
70
+ nodes.
71
+ - `fix` builds security headers from the live response headers and never weakens an existing
72
+ value (for example `X-Frame-Options: DENY`).
73
+ - `fix` keeps a hand-written `llms.txt` and only appends pages it does not link yet.
74
+ - Running from a folder whose `seoforge.yaml` describes another site no longer applies that
75
+ site's entity, competitors and keywords.
76
+ - Markdown mirrors and `llms-full.txt` skip hidden elements and keep words in flex/grid
77
+ layouts apart.
78
+ - Calls to action phrased as questions ("Ready to start?") are no longer treated as AEO
79
+ questions.
80
+ - Images sized by CSS (Next.js `fill`, Tailwind `w-N h-N`, inline width/height or
81
+ aspect-ratio) are no longer reported as missing dimensions.
82
+ - Cloudflare `/cdn-cgi/` endpoints are never crawled.
83
+ - GitHub Actions use checkout, setup-python and upload-artifact v7 (Node 24).
84
+
85
+ ### Fixed
86
+
87
+ - The Organization generator dropped the address and founders already published on the site.
88
+ - The Wikidata draft lowercased brand names ("brightWorks") and repeated "<Name> is a".
89
+ - JSON-LD output now escapes `</` so crawled text cannot close the script tag.
90
+
91
+ ## [0.1.0] - 2026-10-03
92
+
93
+ First release: robots-aware crawler, technical / on-page / performance audit, structured data
94
+ validation and generators, AEO and E-E-A-T checks, GEO (AI crawler policy, llms.txt, entity
95
+ tools, citation score, AI-visibility tracker), indexing (IndexNow, Search Console, Bing),
96
+ off-page helpers, `seoforge fix`, HTML / Markdown / JSON reports, Docker image, GitHub Action
97
+ and CI.
98
+
99
+ [0.3.0]: https://github.com/Dreadonyx/ranq/compare/v0.2.0...v0.3.0
100
+ [0.2.0]: https://github.com/Dreadonyx/ranq/compare/2e7659f...v0.2.0
101
+ [0.1.0]: https://github.com/Dreadonyx/ranq/tree/2e7659f
ranq-0.3.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Dreadonyx
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
ranq-0.3.0/PKG-INFO ADDED
@@ -0,0 +1,322 @@
1
+ Metadata-Version: 2.5
2
+ Name: ranq
3
+ Version: 0.3.0
4
+ Summary: Free, open-source CLI that audits and optimizes websites for SEO, AEO and GEO.
5
+ Project-URL: Homepage, https://github.com/Dreadonyx/ranq
6
+ Project-URL: Repository, https://github.com/Dreadonyx/ranq
7
+ Project-URL: Issues, https://github.com/Dreadonyx/ranq/issues
8
+ Project-URL: Changelog, https://github.com/Dreadonyx/ranq/blob/main/CHANGELOG.md
9
+ Project-URL: NodeKraft, https://nodekraft.tech
10
+ Author: NodeKraft
11
+ Maintainer: Dreadonyx
12
+ License-Expression: MIT
13
+ License-File: LICENSE
14
+ Keywords: aeo,ai-search,audit,core-web-vitals,crawler,geo,indexnow,json-ld,llms.txt,search-console,seo,structured-data
15
+ Classifier: Development Status :: 4 - Beta
16
+ Classifier: Environment :: Console
17
+ Classifier: Intended Audience :: Developers
18
+ Classifier: Intended Audience :: Information Technology
19
+ Classifier: Operating System :: OS Independent
20
+ Classifier: Programming Language :: Python :: 3
21
+ Classifier: Programming Language :: Python :: 3 :: Only
22
+ Classifier: Programming Language :: Python :: 3.11
23
+ Classifier: Programming Language :: Python :: 3.12
24
+ Classifier: Programming Language :: Python :: 3.13
25
+ Classifier: Topic :: Internet :: WWW/HTTP :: Indexing/Search
26
+ Classifier: Topic :: Internet :: WWW/HTTP :: Site Management
27
+ Classifier: Topic :: Internet :: WWW/HTTP :: Site Management :: Link Checking
28
+ Classifier: Typing :: Typed
29
+ Requires-Python: >=3.11
30
+ Requires-Dist: httpx>=0.27
31
+ Requires-Dist: jinja2>=3.1.6
32
+ Requires-Dist: playwright>=1.44
33
+ Requires-Dist: pydantic>=2.7
34
+ Requires-Dist: pyyaml>=6.0
35
+ Requires-Dist: rich>=13.7
36
+ Requires-Dist: selectolax>=1.0
37
+ Requires-Dist: typer>=0.12
38
+ Requires-Dist: yake>=0.4.8
39
+ Provides-Extra: dev
40
+ Requires-Dist: mypy>=1.10; extra == 'dev'
41
+ Requires-Dist: pre-commit>=3.7; extra == 'dev'
42
+ Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
43
+ Requires-Dist: pytest>=8.2; extra == 'dev'
44
+ Requires-Dist: ruff>=0.5; extra == 'dev'
45
+ Requires-Dist: types-pyyaml; extra == 'dev'
46
+ Provides-Extra: google
47
+ Requires-Dist: google-auth>=2.29; extra == 'google'
48
+ Provides-Extra: ml
49
+ Requires-Dist: sentence-transformers>=3.0; extra == 'ml'
50
+ Description-Content-Type: text/markdown
51
+
52
+ # Ranq
53
+
54
+ [![CI](https://github.com/Dreadonyx/ranq/actions/workflows/ci.yml/badge.svg)](https://github.com/Dreadonyx/ranq/actions/workflows/ci.yml)
55
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
56
+ ![Python 3.11+](https://img.shields.io/badge/python-3.11%2B-blue.svg)
57
+
58
+ **Free, open-source CLI that audits and optimizes websites for search engines (SEO), answer
59
+ engines (AEO) and AI / generative engines (GEO) - and submits them to every index it
60
+ legitimately can.**
61
+
62
+ No paid APIs. No accounts required for the core audit. MIT licensed.
63
+
64
+ Built and maintained by [NodeKraft](https://nodekraft.tech), a security-first digital agency in
65
+ Chennai. Want the findings fixed for you? [Talk to us](https://nodekraft.tech).
66
+
67
+ > **Honesty first.** No tool can guarantee rankings, indexing of every page or keyword, or that
68
+ > any AI system will know, cite or recommend your site. Ranq maximizes *eligibility,
69
+ > discoverability and citation likelihood*, labels every heuristic as an estimate, cites the
70
+ > guideline behind every finding, and never uses spam tactics. See
71
+ > [What Ranq cannot do](#what-ranq-cannot-do).
72
+
73
+ ```
74
+ ranq audit https://example.com # crawl + prioritized HTML/Markdown/JSON report
75
+ ranq fix https://example.com # robots.txt, sitemap, llms.txt, JSON-LD, redirects...
76
+ ranq diagnose https://example.com/page # why isn't this URL indexed?
77
+ ```
78
+
79
+ ![Ranq HTML report](docs/report-screenshot.png)
80
+
81
+ *The HTML report for the bundled test site, which has planted problems. Open the full
82
+ [example report](docs/example-report/report.html) (download it and open it in a browser) or
83
+ read the [Markdown version](docs/example-report/report.md). Changes between versions are in
84
+ the [CHANGELOG](CHANGELOG.md).*
85
+
86
+ ## What it does
87
+
88
+ | Area | Highlights |
89
+ |---|---|
90
+ | **Crawler** | Async, robots.txt-aware (RFC 9309 wildcards + longest match), per-host rate limit and `Crawl-delay`, redirect-chain capture, SQLite cache, resumable (`--resume`), optional JavaScript rendering with Playwright, site graph (click depth, inlinks, orphans) |
91
+ | **Technical SEO** | Status codes, broken links, redirect chains/loops, soft 404s (site-wide probe + heuristics), HTTPS/HSTS/mixed content, www/http canonicalization, robots.txt validity, sitemap validity and consistency, canonicals, meta robots / X-Robots-Tag, hreflang, viewport, lang, images, compression, security headers, duplicate and near-duplicate content (SimHash), sites on a platform subdomain instead of their own domain (~80 hosts: Vercel, Netlify, Hostinger, GitHub Pages, Wix...), unreachable sites |
92
+ | **Performance** | Core Web Vitals field data (CrUX via the free PageSpeed Insights API) and lab data (PageSpeed / local Lighthouse CLI); H1 served invisible until JavaScript runs (entrance animations that delay LCP); images sized by width/height, CSS aspect-ratio or utility classes |
93
+ | **On-page & keywords** | Titles, meta descriptions, headings, thin content, JS-only content, anchors, internal linking, Open Graph (including og:url pointing to another page); keyword extraction (TF-IDF, YAKE, n-grams), rule-based intent, topic clustering (local sentence-transformers if installed, else TF-IDF), cannibalization, competitor content gap, opt-in cached Google Autosuggest ideas, keyword-to-page map |
94
+ | **Structured data** | JSON-LD extraction and validation (required/recommended properties, nested entities, ISO dates, `@graph`, FAQ visibility policy, WebPage nodes describing another URL); generators for Organization, WebSite (+SearchAction), WebPage, Article, Product, FAQPage, HowTo, BreadcrumbList, Person with `sameAs`, merged with the markup already on the site into one `@graph` for the layout and one per page |
95
+ | **AEO** | Question-style headings (calls to action such as "Ready to start?" excluded), 40-60 word answer blocks, answer drafts trimmed from *your own* text, FAQ/definition/steps/table detection, E-E-A-T checks (author, author page, dates, citations, About/Contact/Privacy) |
96
+ | **GEO** | Per-bot robots.txt policy for 15 AI crawlers/tokens (GPTBot, OAI-SearchBot, ChatGPT-User, ClaudeBot, Claude-SearchBot, Claude-User, PerplexityBot, Perplexity-User, Google-Extended, Applebot-Extended, CCBot, Bytespider, Meta-ExternalAgent, Amazonbot, cohere-ai); `/llms.txt`, `/llms-full.txt` and Markdown mirrors; entity fact sheet; sameAs graph, brand-consistency checklist and a check that markup, llms.txt and config describe the brand the same way; contact email hidden by Cloudflare obfuscation; Wikidata search + QuickStatements draft; citation-friendliness score; Common Crawl presence; AI-visibility tracker (SQLite) |
97
+ | **Indexing** | Sitemap generator, IndexNow (key file + verified batch submission of changed URLs), Google Search Console API (sitemap submit, quota-aware URL Inspection), Bing Webmaster API, engine coverage table, "why isn't this indexed?" diagnostic |
98
+ | **Off-page** | Opportunity checklist, curated directory list, robots-aware broken-link finder with Wayback lookups, unlinked brand-mention finder (Hacker News + Wikipedia APIs), strict anti-spam policy |
99
+ | **Reporting** | Prioritized issues (Critical/High/Medium/Low) with impact, effort, exact fix, source and evidence; health score where site-wide issues count in full, plus a score for every page; framework detection; standalone HTML dashboard + Markdown + JSON; 30/60/90-day plan; "what this tool cannot do" section |
100
+ | **`ranq fix`** | Ready-to-paste robots.txt, sitemap.xml, llms.txt (extends a hand-written one instead of replacing it), JSON-LD, meta tags, security headers (built from your live headers, never weakening them) and redirects for nginx, Apache, Vercel, Netlify and Cloudflare. A FRAMEWORK.md says where each file goes in the detected stack, and Next.js App Router sites also get `app/robots.ts`, `app/sitemap.ts` and a JSON-LD component. Applies to a *local* directory only after a diff preview and per-file confirmation |
101
+
102
+ ## Install
103
+
104
+ Requires Python 3.11+.
105
+
106
+ ```bash
107
+ pipx install ranq # or: pip install ranq
108
+ pipx install git+https://github.com/Dreadonyx/ranq # latest main, before a PyPI release
109
+ # Optional extras:
110
+ playwright install chromium # JavaScript rendering (--render)
111
+ pipx inject ranq google-auth # Search Console via service account (pip: "ranq[google]")
112
+ pipx inject ranq sentence-transformers # local semantic clustering (large: PyTorch)
113
+ npm install -g lighthouse # local Lighthouse lab runs (--lighthouse)
114
+ ```
115
+
116
+ From source:
117
+
118
+ ```bash
119
+ git clone https://github.com/Dreadonyx/ranq && cd ranq
120
+ python -m venv .venv && source .venv/bin/activate
121
+ pip install -e ".[google]"
122
+ ```
123
+
124
+ Or with Docker:
125
+
126
+ ```bash
127
+ docker build -t ranq . # add --build-arg WITH_BROWSER=1 for --render
128
+ docker run --rm --user "$(id -u):$(id -g)" -v "$PWD:/work" ranq audit https://example.com
129
+ ```
130
+
131
+ ## Quickstart
132
+
133
+ ```bash
134
+ ranq init --site https://example.com # a few questions, pre-filled from your home page (optional)
135
+ ranq audit https://example.com # report in ./ranq-report/report.html
136
+ ranq audit https://example.com --render --max-pages 1000 --competitor https://rival.com/page
137
+ ranq fix https://example.com # files in ./ranq-fixes (see INDEX.md)
138
+ ranq fix https://example.com --apply-to ./public # diff + confirm, then write locally
139
+ ranq diagnose https://example.com/missing-page --crawl-pages 200
140
+ ranq geo bots https://example.com # which AI crawlers can access the site
141
+ ```
142
+
143
+ ## Commands
144
+
145
+ | Command | Purpose |
146
+ |---|---|
147
+ | `audit URL` | Crawl and report. Options: `--max-pages`, `--depth`, `--delay`, `--concurrency`, `--render`, `--resume`, `--cached`, `--check-external`, `--competitor URL` (repeatable), `--suggest`, `--lighthouse`, `--no-pagespeed`, `--format html/md/json`, `--fail-on critical/high/medium/low`, `--compare old.json` / `--no-compare` (by default each audit is compared with the previous report for the same site; every run is also kept in `<out>/history/`) |
148
+ | `fix URL` | Generate fix files. `--ai-policy allow-all/search-only/deny-all`, `--redirect-map old_new.csv`, `--apply-to DIR [--yes]` |
149
+ | `diagnose URL` | Robots, status, redirects, noindex, canonical, rendering, soft 404, sitemap, internal links, optional `--gsc` URL Inspection |
150
+ | `geo bots / llms / entity / commoncrawl` | AI crawler access table; llms.txt + mirrors only; entity + Wikidata; Common Crawl presence |
151
+ | `visibility init / add / prompts / log / import / run / report` | AI-visibility tracker (see below) |
152
+ | `index engines / indexnow-key / indexnow / gsc-sitemap / gsc-inspect / bing` | Submissions. All ask for confirmation; all support `--dry-run` |
153
+ | `offpage checklist / policy / broken-links / mentions` | Ethical link-building helpers |
154
+
155
+ ### AI-visibility tracker
156
+
157
+ AI assistants have no free, permitted API for "does this assistant mention my brand", and
158
+ automating their consumer apps violates their terms. So the tracker is a log:
159
+
160
+ ```bash
161
+ ranq visibility init # prompts from entity.name/category/city
162
+ ranq visibility add "best kanban app for small teams"
163
+ ranq visibility prompts
164
+ ranq visibility log 3 perplexity --mentioned --cited-url https://example.com/guide
165
+ ranq visibility import answers.csv # prompt,engine,date,mentioned,cited_url,position,notes
166
+ ranq visibility run gemini --cmd "python my_gemini_free_tier.py {prompt}" # your own permitted tool
167
+ ranq visibility report --json visibility.json
168
+ ```
169
+
170
+ `run` executes *your* command once per prompt (no shell; `{prompt}` is passed as one
171
+ argument) and detects brand mentions and links to your domain in its output.
172
+
173
+ ## Configuration (`ranq.yaml`)
174
+
175
+ Every field is optional; `ranq init` writes one by asking a few questions (pre-filled from what your home page already says), and `ranq init --example` writes a commented file with every setting. API keys are read from
176
+ environment variables, never from the YAML.
177
+
178
+ ```yaml
179
+ site: https://example.com
180
+ crawl: {max_pages: 500, delay_seconds: 0.5, concurrency: 4, render_js: false, exclude: ["/wp-admin"]}
181
+ entity:
182
+ name: Example Inc.
183
+ description: One factual sentence about what you do.
184
+ category: project management software
185
+ city: Chennai
186
+ founded: "2019"
187
+ same_as: [https://github.com/example, https://www.linkedin.com/company/example]
188
+ geo:
189
+ ai_bot_preset: search-only # allow-all | search-only | deny-all | custom
190
+ ai_bots: {CCBot: deny} # per-bot overrides
191
+ competitors: [https://competitor.example/pricing]
192
+ seed_keywords: [project management]
193
+ report: {output_dir: ranq-report, fail_on: never}
194
+ disabled_checks: [deep-pages]
195
+ plugins: [mycompany.ranq_checks]
196
+ ```
197
+
198
+ `search-only` (the default) allows AI search and user-initiated fetchers - so answers can cite
199
+ and link you - and blocks training crawlers. Allowed bots inherit your `*` rules, so private
200
+ paths stay private.
201
+
202
+ ## Free API keys
203
+
204
+ All optional; the audit works without any of them.
205
+
206
+ **PageSpeed Insights (Core Web Vitals)** - works without a key at low volume. For a higher
207
+ free quota: Google Cloud Console → create a project → *APIs & Services* → enable **PageSpeed
208
+ Insights API** → *Credentials* → **Create API key**. Then `export PAGESPEED_API_KEY=...`.
209
+
210
+ **Google Search Console** (sitemap submission, URL Inspection):
211
+ 1. Verify your site in [Search Console](https://search.google.com/search-console).
212
+ 2. Google Cloud Console → enable the **Google Search Console API**.
213
+ 3. Either: create a **service account**, download its JSON key, `export
214
+ GSC_SERVICE_ACCOUNT_FILE=/path/key.json`, `pip install -e ".[google]"`, and add the
215
+ service account's email under Search Console → *Settings → Users and permissions* (Full
216
+ permission; Owner if sitemap submission is refused).
217
+ Or: `gcloud auth application-default login --scopes=https://www.googleapis.com/auth/webmasters,https://www.googleapis.com/auth/cloud-platform`
218
+ then `export GSC_ACCESS_TOKEN=$(gcloud auth application-default print-access-token)`.
219
+ 4. Quotas (enforced locally): URL Inspection 2,000/day and 600/minute per property.
220
+
221
+ **Bing Webmaster Tools**: verify the site at [bing.com/webmasters](https://www.bing.com/webmasters)
222
+ (you can import from Search Console) → *Settings → API access* → generate an **API key** →
223
+ `export BING_WEBMASTER_API_KEY=...`.
224
+
225
+ **IndexNow** (Bing, Yandex, Seznam.cz, Naver, Yep): `ranq index indexnow-key`, upload
226
+ the generated `<key>.txt` to your site root, `export INDEXNOW_KEY=<key>`, then
227
+ `ranq index indexnow https://example.com` (submits only URLs whose content changed).
228
+
229
+ ## GitHub Action
230
+
231
+ Audit every deploy and upload the report as an artifact (see `docs/github-action.md`):
232
+
233
+ ```yaml
234
+ - uses: Dreadonyx/ranq@main
235
+ with:
236
+ url: https://example.com
237
+ fail-on: critical
238
+ pagespeed-api-key: ${{ secrets.PAGESPEED_API_KEY }}
239
+ ```
240
+
241
+ ## Plugins
242
+
243
+ Checks are plain functions registered with `@check`. Ship them in any module and list it under
244
+ `plugins:` or expose it via the `ranq.checks` entry-point group. See
245
+ [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) and `tests/plugins/demo_plugin.py`.
246
+
247
+ ## Ethics
248
+
249
+ - Obeys robots.txt on every request and redirect hop (including competitor, mention and
250
+ broken-link checks), rate-limits per host and honours `Crawl-delay`.
251
+ - Never cloaks, generates doorway pages, fake reviews, or spam links; never scrapes search
252
+ result pages or consumer AI apps.
253
+ - Never uses Google's Indexing API (it is only for JobPosting and BroadcastEvent pages).
254
+ - Generated markup and copy use only data from your pages or `ranq.yaml`; anything unknown
255
+ is left as a TODO rather than invented.
256
+ - Never modifies a site without a diff preview and confirmation, and only in a local directory.
257
+
258
+ ## Assumptions
259
+
260
+ - "Submit to every index" means the legitimate channels that exist: Google Search Console,
261
+ Bing Webmaster Tools, IndexNow, sitemaps and robots.txt. Other engines either use those
262
+ indexes or offer no submission (listed by `ranq index engines`).
263
+ - Keyword volumes are omitted rather than estimated, because no free, reliable source exists.
264
+ - The default AI crawler policy is `search-only`; change it in `ranq.yaml`.
265
+ - The crawler identifies itself as `Ranq/<version>` and matches robots.txt groups for
266
+ `Ranq` (falling back to `*`).
267
+ - Response cache is written on every crawl but only read with `--resume` / `--cached`, so a
268
+ normal audit always reflects the live site.
269
+ - Thresholds such as "title > 60 characters", "under 200 words" or "40-60 word answers" are
270
+ common heuristics, not search engine rules; findings based on them are labelled estimates.
271
+
272
+ ## What Ranq cannot do
273
+
274
+ - Guarantee rankings, indexing of any page, or traffic.
275
+ - Make any LLM know, cite or recommend your site; it can only make your content easier to
276
+ retrieve, quote and attribute.
277
+ - Provide search volume, keyword difficulty or a full backlink index (use Search Console and
278
+ Bing Webmaster Tools "Links" reports for your real backlinks).
279
+ - Enforce robots.txt: bots that ignore it must be blocked at the server/CDN/WAF.
280
+ - Promise anything from llms.txt: it is a community proposal that major AI providers have not
281
+ confirmed using.
282
+ - Bring back FAQ/HowTo rich results in Google (restricted/removed in 2023), or the sitelinks
283
+ search box (retired in 2024).
284
+ - Render pages exactly like Googlebot; headless Chromium is a close approximation.
285
+ - Measure Core Web Vitals field data for low-traffic URLs (CrUX has no data for them).
286
+
287
+ ## Roadmap
288
+
289
+ - Incremental crawls driven by sitemap `lastmod` and HTTP conditional requests
290
+ - Log-file analysis (real Googlebot/AI-bot hits vs. robots policy)
291
+ - Search Console Search Analytics import (queries, CTR, position) into the keyword map
292
+ - More rich-result validators (Event, JobPosting, Recipe, VideoObject details) and SHACL export
293
+ - Multi-language keyword extraction (stopword lists beyond English)
294
+ - Trend charts across the audit history (`<out>/history/`)
295
+ - WordPress / static-site generator integrations for `fix --apply-to`
296
+
297
+ ## Development
298
+
299
+ ```bash
300
+ pip install -e ".[dev]" && playwright install chromium
301
+ pre-commit install
302
+ pytest -q # spins up a local test site with planted issues
303
+ ruff check src tests && ruff format --check src tests && mypy
304
+ python tests/serve_site.py 8765 & ranq audit http://127.0.0.1:8765 --no-pagespeed --delay 0
305
+ ```
306
+
307
+ ### Releasing
308
+
309
+ 1. Bump `__version__` in `src/ranq/__init__.py` and add a section to `CHANGELOG.md`.
310
+ 2. Commit, then `git tag v<version> && git push origin v<version>`.
311
+ 3. The Release workflow checks the tag matches the version, runs the tests, builds, creates the
312
+ GitHub Release with the changelog notes and the sdist/wheel attached, and publishes to PyPI.
313
+
314
+ One-time PyPI setup (no API token needed): on pypi.org, *Your projects -> Publishing -> Add a
315
+ new pending publisher* with owner `Dreadonyx`, repository `ranq`, workflow `release.yml`,
316
+ environment `pypi`. Then in the GitHub repository settings add the variable
317
+ `PUBLISH_TO_PYPI` = `true` (*Settings -> Secrets and variables -> Actions -> Variables*).
318
+ Until then the PyPI job is skipped and releases go to GitHub only.
319
+
320
+ ## License
321
+
322
+ MIT - see [LICENSE](LICENSE).