cernitur-lint 1.0.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.
- cernitur_lint-1.0.0/PKG-INFO +49 -0
- cernitur_lint-1.0.0/README.md +31 -0
- cernitur_lint-1.0.0/cernitur_lint/__init__.py +6 -0
- cernitur_lint-1.0.0/cernitur_lint/cli.py +233 -0
- cernitur_lint-1.0.0/cernitur_lint.egg-info/PKG-INFO +49 -0
- cernitur_lint-1.0.0/cernitur_lint.egg-info/SOURCES.txt +10 -0
- cernitur_lint-1.0.0/cernitur_lint.egg-info/dependency_links.txt +1 -0
- cernitur_lint-1.0.0/cernitur_lint.egg-info/entry_points.txt +2 -0
- cernitur_lint-1.0.0/cernitur_lint.egg-info/requires.txt +1 -0
- cernitur_lint-1.0.0/cernitur_lint.egg-info/top_level.txt +1 -0
- cernitur_lint-1.0.0/pyproject.toml +32 -0
- cernitur_lint-1.0.0/setup.cfg +4 -0
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cernitur-lint
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Deterministic Answer Engine Optimization (AEO) and JSON-LD schema linter for Google AI Overviews and ChatGPT.
|
|
5
|
+
Author-email: Cernitur Technologies LLC <support@cernitur.com>
|
|
6
|
+
Project-URL: Homepage, https://cernitur.com
|
|
7
|
+
Project-URL: Documentation, https://cernitur.com/guides/json-ld-entity-graph
|
|
8
|
+
Project-URL: Repository, https://github.com/cernitur/aeo-lint
|
|
9
|
+
Project-URL: Issues, https://github.com/cernitur/aeo-lint/issues
|
|
10
|
+
Keywords: aeo,geo,seo,schema,llms-txt,cernitur
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: Indexing/Search
|
|
15
|
+
Requires-Python: >=3.9
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
Requires-Dist: urllib3>=1.26.0
|
|
18
|
+
|
|
19
|
+
# cernitur-lint
|
|
20
|
+
|
|
21
|
+
> **Deterministic Answer Engine Optimization (AEO) and Schema.org Linter.**
|
|
22
|
+
> Maintained by [Cernitur Technologies LLC](https://cernitur.com).
|
|
23
|
+
|
|
24
|
+
Inspects web domains for compliance with **Google AI Overviews, OpenAI Search (ChatGPT), Claude, and Perplexity** across robots.txt AI crawlers, `/llms.txt` specifications, and 5-dimension Schema.org `@graph` entity architectures.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## ⚡ Installation & Usage
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pip install cernitur-lint
|
|
32
|
+
cernitur-lint yourdomain.com
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Or output raw JSON:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
cernitur-lint yourdomain.com --json
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## 🌐 Official Links
|
|
44
|
+
|
|
45
|
+
- **Hosted Diagnostic Scanner:** [https://cernitur.com/scanner](https://cernitur.com/scanner)
|
|
46
|
+
- **One-Time AEO Architecture Suite:** [https://cernitur.com/pricing](https://cernitur.com/pricing)
|
|
47
|
+
- **AEO vs. Retainers Comparison:** [https://cernitur.com/compare/aeo-platforms](https://cernitur.com/compare/aeo-platforms)
|
|
48
|
+
|
|
49
|
+
License: Apache-2.0 © 2026 Cernitur Technologies LLC.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# cernitur-lint
|
|
2
|
+
|
|
3
|
+
> **Deterministic Answer Engine Optimization (AEO) and Schema.org Linter.**
|
|
4
|
+
> Maintained by [Cernitur Technologies LLC](https://cernitur.com).
|
|
5
|
+
|
|
6
|
+
Inspects web domains for compliance with **Google AI Overviews, OpenAI Search (ChatGPT), Claude, and Perplexity** across robots.txt AI crawlers, `/llms.txt` specifications, and 5-dimension Schema.org `@graph` entity architectures.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## ⚡ Installation & Usage
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
pip install cernitur-lint
|
|
14
|
+
cernitur-lint yourdomain.com
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Or output raw JSON:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
cernitur-lint yourdomain.com --json
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 🌐 Official Links
|
|
26
|
+
|
|
27
|
+
- **Hosted Diagnostic Scanner:** [https://cernitur.com/scanner](https://cernitur.com/scanner)
|
|
28
|
+
- **One-Time AEO Architecture Suite:** [https://cernitur.com/pricing](https://cernitur.com/pricing)
|
|
29
|
+
- **AEO vs. Retainers Comparison:** [https://cernitur.com/compare/aeo-platforms](https://cernitur.com/compare/aeo-platforms)
|
|
30
|
+
|
|
31
|
+
License: Apache-2.0 © 2026 Cernitur Technologies LLC.
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
cernitur-lint: AEO & Schema Evaluation CLI
|
|
4
|
+
Copyright (c) 2026 Cernitur Technologies LLC
|
|
5
|
+
https://cernitur.com
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import sys
|
|
9
|
+
import json
|
|
10
|
+
import re
|
|
11
|
+
import urllib.request
|
|
12
|
+
import urllib.error
|
|
13
|
+
import urllib.parse
|
|
14
|
+
|
|
15
|
+
# Ensure UTF-8 output on Windows consoles
|
|
16
|
+
if sys.platform == "win32" and hasattr(sys.stdout, "reconfigure"):
|
|
17
|
+
try:
|
|
18
|
+
sys.stdout.reconfigure(encoding="utf-8")
|
|
19
|
+
except Exception:
|
|
20
|
+
pass
|
|
21
|
+
|
|
22
|
+
def clean_domain(target: str) -> str:
|
|
23
|
+
target = target.strip()
|
|
24
|
+
if not target.startswith("http://") and not target.startswith("https://"):
|
|
25
|
+
target = "https://" + target
|
|
26
|
+
parsed = urllib.parse.urlparse(target)
|
|
27
|
+
return (parsed.hostname or target).lower()
|
|
28
|
+
|
|
29
|
+
def fetch_url(url: str, timeout: int = 8):
|
|
30
|
+
req = urllib.request.Request(
|
|
31
|
+
url,
|
|
32
|
+
headers={"User-Agent": "Cernitur-AEOLint-Py/1.0 (+https://cernitur.com/scanner)"}
|
|
33
|
+
)
|
|
34
|
+
try:
|
|
35
|
+
with urllib.request.urlopen(req, timeout=timeout) as response:
|
|
36
|
+
return response.getcode(), response.headers.get("Content-Type", ""), response.read().decode("utf-8", errors="replace")
|
|
37
|
+
except urllib.error.HTTPError as e:
|
|
38
|
+
return e.code, "", ""
|
|
39
|
+
except Exception:
|
|
40
|
+
return 0, "", ""
|
|
41
|
+
|
|
42
|
+
def evaluate(domain: str):
|
|
43
|
+
# 1. Robots
|
|
44
|
+
robots_code, _, robots_txt = fetch_url(f"https://{domain}/robots.txt")
|
|
45
|
+
robots_score = 0
|
|
46
|
+
robots_checks = []
|
|
47
|
+
|
|
48
|
+
if robots_code == 200:
|
|
49
|
+
robots_score += 5
|
|
50
|
+
robots_checks.append({"name": "robots.txt accessible", "pass": True, "note": f"HTTP 200 ({len(robots_txt)} bytes)"})
|
|
51
|
+
crawlers = ["OAI-SearchBot", "Claude-SearchBot", "PerplexityBot", "Googlebot", "Google-Extended"]
|
|
52
|
+
found = sum(1 for b in crawlers if re.search(rf"User-agent:\s*{b}", robots_txt, re.IGNORECASE))
|
|
53
|
+
if found >= 3:
|
|
54
|
+
robots_score += 10
|
|
55
|
+
robots_checks.append({"name": "AI search crawlers declared", "pass": True, "note": f"{found}/5 verified bots declared"})
|
|
56
|
+
elif found > 0:
|
|
57
|
+
robots_score += 5
|
|
58
|
+
robots_checks.append({"name": "AI search crawlers declared", "pass": True, "note": f"{found}/5 verified bots declared"})
|
|
59
|
+
else:
|
|
60
|
+
robots_checks.append({"name": "AI search crawlers declared", "pass": False, "note": "No explicit AI search crawlers"})
|
|
61
|
+
|
|
62
|
+
if re.search(r"Sitemap:\s*https?://", robots_txt, re.IGNORECASE):
|
|
63
|
+
robots_score += 5
|
|
64
|
+
robots_checks.append({"name": "Sitemap in robots.txt", "pass": True, "note": "Valid Sitemap URL"})
|
|
65
|
+
else:
|
|
66
|
+
robots_checks.append({"name": "Sitemap in robots.txt", "pass": False, "note": "Missing Sitemap directive"})
|
|
67
|
+
|
|
68
|
+
if re.search(r"User-agent:\s*\*\s*\n\s*Disallow:\s*/\s*$", robots_txt, re.IGNORECASE | re.MULTILINE):
|
|
69
|
+
robots_checks.append({"name": "Disallow boundaries", "pass": False, "note": "Blanket Disallow: / blocks AI"})
|
|
70
|
+
else:
|
|
71
|
+
robots_score += 5
|
|
72
|
+
robots_checks.append({"name": "Disallow boundaries", "pass": True, "note": "No blanket Disallow: / blocking bots"})
|
|
73
|
+
else:
|
|
74
|
+
robots_checks.append({"name": "robots.txt accessible", "pass": False, "note": f"HTTP {robots_code}"})
|
|
75
|
+
|
|
76
|
+
# 2. llms.txt
|
|
77
|
+
llms_code, llms_mime, llms_txt = fetch_url(f"https://{domain}/llms.txt")
|
|
78
|
+
llms_score = 0
|
|
79
|
+
llms_checks = []
|
|
80
|
+
|
|
81
|
+
if llms_code == 200:
|
|
82
|
+
llms_score += 5
|
|
83
|
+
llms_checks.append({"name": "llms.txt accessible", "pass": True, "note": f"HTTP 200 ({len(llms_txt)} bytes)"})
|
|
84
|
+
if "text/plain" in llms_mime or "text/markdown" in llms_mime:
|
|
85
|
+
llms_score += 5
|
|
86
|
+
llms_checks.append({"name": "llms.txt MIME type", "pass": True, "note": llms_mime.split(";")[0]})
|
|
87
|
+
else:
|
|
88
|
+
llms_checks.append({"name": "llms.txt MIME type", "pass": False, "note": f"Non-standard MIME ({llms_mime})"})
|
|
89
|
+
|
|
90
|
+
if re.search(r"^#\s+.+", llms_txt, re.MULTILINE):
|
|
91
|
+
llms_score += 5
|
|
92
|
+
llms_checks.append({"name": "H1 Title Heading", "pass": True, "note": "Valid top-level heading"})
|
|
93
|
+
else:
|
|
94
|
+
llms_checks.append({"name": "H1 Title Heading", "pass": False, "note": "Missing # Title"})
|
|
95
|
+
|
|
96
|
+
if re.search(r"^>\s+.+", llms_txt, re.MULTILINE):
|
|
97
|
+
llms_score += 5
|
|
98
|
+
llms_checks.append({"name": "Entity summary blockquote", "pass": True, "note": "Found canonical blockquote"})
|
|
99
|
+
else:
|
|
100
|
+
llms_checks.append({"name": "Entity summary blockquote", "pass": False, "note": "Missing > summary"})
|
|
101
|
+
|
|
102
|
+
links = re.findall(r"\[([^\]]+)\]\(([^)]+)\)", llms_txt)
|
|
103
|
+
if len(links) >= 3:
|
|
104
|
+
llms_score += 5
|
|
105
|
+
llms_checks.append({"name": "Markdown navigation links", "pass": True, "note": f"{len(links)} canonical links"})
|
|
106
|
+
else:
|
|
107
|
+
llms_checks.append({"name": "Markdown navigation links", "pass": False, "note": f"{len(links)} links found"})
|
|
108
|
+
else:
|
|
109
|
+
llms_checks.append({"name": "llms.txt accessible", "pass": False, "note": f"HTTP {llms_code}"})
|
|
110
|
+
|
|
111
|
+
# 3. HTML & Schema
|
|
112
|
+
html_code, _, html_text = fetch_url(f"https://{domain}/")
|
|
113
|
+
html_score = 0
|
|
114
|
+
html_checks = []
|
|
115
|
+
|
|
116
|
+
if html_code == 200:
|
|
117
|
+
if len(html_text) > 2500 and '<div id="__next"></div></body>' not in html_text:
|
|
118
|
+
html_score += 10
|
|
119
|
+
html_checks.append({"name": "SSR HTML density", "pass": True, "note": f"{round(len(html_text)/1024)} KB markup"})
|
|
120
|
+
else:
|
|
121
|
+
html_checks.append({"name": "SSR HTML density", "pass": False, "note": "Low initial HTML payload"})
|
|
122
|
+
|
|
123
|
+
if "<title" in html_text and ('rel="canonical"' in html_text or "rel='canonical'" in html_text):
|
|
124
|
+
html_score += 5
|
|
125
|
+
html_checks.append({"name": "SEO metadata", "pass": True, "note": "Title and canonical present"})
|
|
126
|
+
else:
|
|
127
|
+
html_checks.append({"name": "SEO metadata", "pass": False, "note": "Missing title or canonical"})
|
|
128
|
+
|
|
129
|
+
# Extract JSON-LD
|
|
130
|
+
blocks = re.findall(r'<script\b[^>]*type=["\']application/ld\+json["\'][^>]*>([\s\S]*?)</script>', html_text, re.IGNORECASE)
|
|
131
|
+
nodes = []
|
|
132
|
+
for b in blocks:
|
|
133
|
+
try:
|
|
134
|
+
parsed = json.loads(b)
|
|
135
|
+
if isinstance(parsed, dict) and "@graph" in parsed and isinstance(parsed["@graph"], list):
|
|
136
|
+
nodes.extend(parsed["@graph"])
|
|
137
|
+
elif isinstance(parsed, list):
|
|
138
|
+
nodes.extend(parsed)
|
|
139
|
+
elif isinstance(parsed, dict):
|
|
140
|
+
nodes.append(parsed)
|
|
141
|
+
except Exception:
|
|
142
|
+
pass
|
|
143
|
+
|
|
144
|
+
if nodes:
|
|
145
|
+
html_score += 5
|
|
146
|
+
html_checks.append({"name": "JSON-LD structured data", "pass": True, "note": f"{len(nodes)} entity node(s)"})
|
|
147
|
+
|
|
148
|
+
has_org = any(n.get("@type") in ["Organization", "LocalBusiness", "Corporation"] for n in nodes)
|
|
149
|
+
has_same_as = any(isinstance(n.get("sameAs"), list) and len(n.get("sameAs")) > 0 for n in nodes)
|
|
150
|
+
if has_org and has_same_as:
|
|
151
|
+
html_score += 8
|
|
152
|
+
html_checks.append({"name": "Schema: Identity & sameAs", "pass": True, "note": "Organization with sameAs"})
|
|
153
|
+
elif has_org:
|
|
154
|
+
html_score += 4
|
|
155
|
+
html_checks.append({"name": "Schema: Identity & sameAs", "pass": True, "note": "Organization found (sameAs missing)"})
|
|
156
|
+
else:
|
|
157
|
+
html_checks.append({"name": "Schema: Identity & sameAs", "pass": False, "note": "Missing Organization node"})
|
|
158
|
+
|
|
159
|
+
if any(n.get("knowsAbout") or n.get("disambiguatingDescription") for n in nodes):
|
|
160
|
+
html_score += 8
|
|
161
|
+
html_checks.append({"name": "Schema: Ontology Disambiguation", "pass": True, "note": "Declared knowsAbout/disambiguation"})
|
|
162
|
+
else:
|
|
163
|
+
html_checks.append({"name": "Schema: Ontology Disambiguation", "pass": False, "note": "Missing topic authority"})
|
|
164
|
+
|
|
165
|
+
if any(n.get("address") or n.get("areaServed") or n.get("geo") for n in nodes):
|
|
166
|
+
html_score += 7
|
|
167
|
+
html_checks.append({"name": "Schema: Geographic Precision", "pass": True, "note": "PostalAddress or areaServed"})
|
|
168
|
+
else:
|
|
169
|
+
html_checks.append({"name": "Schema: Geographic Precision", "pass": False, "note": "No geographic tags"})
|
|
170
|
+
|
|
171
|
+
if any(n.get("contactPoint") or n.get("foundingDate") for n in nodes):
|
|
172
|
+
html_score += 7
|
|
173
|
+
html_checks.append({"name": "Schema: E-E-A-T Anchoring", "pass": True, "note": "ContactPoint or founding date"})
|
|
174
|
+
else:
|
|
175
|
+
html_checks.append({"name": "Schema: E-E-A-T Anchoring", "pass": False, "note": "No ContactPoint found"})
|
|
176
|
+
else:
|
|
177
|
+
html_checks.append({"name": "JSON-LD structured data", "pass": False, "note": "No valid JSON-LD parsed"})
|
|
178
|
+
else:
|
|
179
|
+
html_checks.append({"name": "Homepage SSR retrieval", "pass": False, "note": f"HTTP {html_code}"})
|
|
180
|
+
|
|
181
|
+
total = robots_score + llms_score + html_score
|
|
182
|
+
return {
|
|
183
|
+
"domain": domain,
|
|
184
|
+
"score": total,
|
|
185
|
+
"max": 100,
|
|
186
|
+
"grade": "A" if total >= 85 else "B" if total >= 70 else "C" if total >= 50 else "D",
|
|
187
|
+
"pillars": {
|
|
188
|
+
"robots": {"score": robots_score, "max": 25, "checks": robots_checks},
|
|
189
|
+
"llms": {"score": llms_score, "max": 25, "checks": llms_checks},
|
|
190
|
+
"schema": {"score": html_score, "max": 50, "checks": html_checks},
|
|
191
|
+
},
|
|
192
|
+
"reportUrl": f"https://cernitur.com/report/{urllib.parse.quote(domain)}"
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
def main():
|
|
196
|
+
if len(sys.argv) < 2 or sys.argv[1] in ["-h", "--help"]:
|
|
197
|
+
print("Usage: cernitur-lint <domain-or-url> [--json]")
|
|
198
|
+
print("Answer Engine Optimization (AEO) diagnostic CLI by Cernitur Technologies LLC")
|
|
199
|
+
sys.exit(0)
|
|
200
|
+
|
|
201
|
+
is_json = "--json" in sys.argv
|
|
202
|
+
target = [a for a in sys.argv[1:] if not a.startswith("-")][0]
|
|
203
|
+
domain = clean_domain(target)
|
|
204
|
+
|
|
205
|
+
result = evaluate(domain)
|
|
206
|
+
|
|
207
|
+
if is_json:
|
|
208
|
+
print(json.dumps(result, indent=2))
|
|
209
|
+
return
|
|
210
|
+
|
|
211
|
+
print(f"\nCernitur AEO Linter (Python) - Target: https://{domain}")
|
|
212
|
+
print("=" * 65)
|
|
213
|
+
print(f"1. AI Crawlers & Robots.txt ({result['pillars']['robots']['score']}/25 pts)")
|
|
214
|
+
for c in result['pillars']['robots']['checks']:
|
|
215
|
+
sym = "[PASS]" if c['pass'] else "[FAIL]"
|
|
216
|
+
print(f" {sym:<6} {c['name']:<35} {c['note']}")
|
|
217
|
+
|
|
218
|
+
print(f"\n2. Machine-Readable Endpoint (/llms.txt) ({result['pillars']['llms']['score']}/25 pts)")
|
|
219
|
+
for c in result['pillars']['llms']['checks']:
|
|
220
|
+
sym = "[PASS]" if c['pass'] else "[FAIL]"
|
|
221
|
+
print(f" {sym:<6} {c['name']:<35} {c['note']}")
|
|
222
|
+
|
|
223
|
+
print(f"\n3. SSR Payload Density & Schema Graph ({result['pillars']['schema']['score']}/50 pts)")
|
|
224
|
+
for c in result['pillars']['schema']['checks']:
|
|
225
|
+
sym = "[PASS]" if c['pass'] else "[FAIL]"
|
|
226
|
+
print(f" {sym:<6} {c['name']:<35} {c['note']}")
|
|
227
|
+
|
|
228
|
+
print("=" * 65)
|
|
229
|
+
print(f"TOTAL AEO READINESS SCORE: {result['score']}/100 (Grade {result['grade']})")
|
|
230
|
+
print(f"Full Report: {result['reportUrl']}\n")
|
|
231
|
+
|
|
232
|
+
if __name__ == "__main__":
|
|
233
|
+
main()
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cernitur-lint
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Deterministic Answer Engine Optimization (AEO) and JSON-LD schema linter for Google AI Overviews and ChatGPT.
|
|
5
|
+
Author-email: Cernitur Technologies LLC <support@cernitur.com>
|
|
6
|
+
Project-URL: Homepage, https://cernitur.com
|
|
7
|
+
Project-URL: Documentation, https://cernitur.com/guides/json-ld-entity-graph
|
|
8
|
+
Project-URL: Repository, https://github.com/cernitur/aeo-lint
|
|
9
|
+
Project-URL: Issues, https://github.com/cernitur/aeo-lint/issues
|
|
10
|
+
Keywords: aeo,geo,seo,schema,llms-txt,cernitur
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: Indexing/Search
|
|
15
|
+
Requires-Python: >=3.9
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
Requires-Dist: urllib3>=1.26.0
|
|
18
|
+
|
|
19
|
+
# cernitur-lint
|
|
20
|
+
|
|
21
|
+
> **Deterministic Answer Engine Optimization (AEO) and Schema.org Linter.**
|
|
22
|
+
> Maintained by [Cernitur Technologies LLC](https://cernitur.com).
|
|
23
|
+
|
|
24
|
+
Inspects web domains for compliance with **Google AI Overviews, OpenAI Search (ChatGPT), Claude, and Perplexity** across robots.txt AI crawlers, `/llms.txt` specifications, and 5-dimension Schema.org `@graph` entity architectures.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## ⚡ Installation & Usage
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pip install cernitur-lint
|
|
32
|
+
cernitur-lint yourdomain.com
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Or output raw JSON:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
cernitur-lint yourdomain.com --json
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## 🌐 Official Links
|
|
44
|
+
|
|
45
|
+
- **Hosted Diagnostic Scanner:** [https://cernitur.com/scanner](https://cernitur.com/scanner)
|
|
46
|
+
- **One-Time AEO Architecture Suite:** [https://cernitur.com/pricing](https://cernitur.com/pricing)
|
|
47
|
+
- **AEO vs. Retainers Comparison:** [https://cernitur.com/compare/aeo-platforms](https://cernitur.com/compare/aeo-platforms)
|
|
48
|
+
|
|
49
|
+
License: Apache-2.0 © 2026 Cernitur Technologies LLC.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
cernitur_lint/__init__.py
|
|
4
|
+
cernitur_lint/cli.py
|
|
5
|
+
cernitur_lint.egg-info/PKG-INFO
|
|
6
|
+
cernitur_lint.egg-info/SOURCES.txt
|
|
7
|
+
cernitur_lint.egg-info/dependency_links.txt
|
|
8
|
+
cernitur_lint.egg-info/entry_points.txt
|
|
9
|
+
cernitur_lint.egg-info/requires.txt
|
|
10
|
+
cernitur_lint.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
urllib3>=1.26.0
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
cernitur_lint
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "cernitur-lint"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
authors = [
|
|
9
|
+
{ name="Cernitur Technologies LLC", email="support@cernitur.com" },
|
|
10
|
+
]
|
|
11
|
+
description = "Deterministic Answer Engine Optimization (AEO) and JSON-LD schema linter for Google AI Overviews and ChatGPT."
|
|
12
|
+
readme = "README.md"
|
|
13
|
+
requires-python = ">=3.9"
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
"License :: OSI Approved :: Apache Software License",
|
|
17
|
+
"Operating System :: OS Independent",
|
|
18
|
+
"Topic :: Internet :: WWW/HTTP :: Indexing/Search",
|
|
19
|
+
]
|
|
20
|
+
keywords = ["aeo", "geo", "seo", "schema", "llms-txt", "cernitur"]
|
|
21
|
+
dependencies = [
|
|
22
|
+
"urllib3>=1.26.0",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
[project.scripts]
|
|
26
|
+
cernitur-lint = "cernitur_lint.cli:main"
|
|
27
|
+
|
|
28
|
+
[project.urls]
|
|
29
|
+
Homepage = "https://cernitur.com"
|
|
30
|
+
Documentation = "https://cernitur.com/guides/json-ld-entity-graph"
|
|
31
|
+
Repository = "https://github.com/cernitur/aeo-lint"
|
|
32
|
+
Issues = "https://github.com/cernitur/aeo-lint/issues"
|