seofleet-cli 0.2.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.
- seofleet_cli-0.2.0/.gitignore +8 -0
- seofleet_cli-0.2.0/LICENSE +21 -0
- seofleet_cli-0.2.0/PKG-INFO +181 -0
- seofleet_cli-0.2.0/README.md +146 -0
- seofleet_cli-0.2.0/pyproject.toml +69 -0
- seofleet_cli-0.2.0/src/seofleet/__init__.py +104 -0
- seofleet_cli-0.2.0/src/seofleet/checks/__init__.py +83 -0
- seofleet_cli-0.2.0/src/seofleet/checks/geo/__init__.py +0 -0
- seofleet_cli-0.2.0/src/seofleet/checks/geo/ai_crawler_directives.py +89 -0
- seofleet_cli-0.2.0/src/seofleet/checks/geo/content_extraction.py +72 -0
- seofleet_cli-0.2.0/src/seofleet/checks/geo/faq_schema.py +58 -0
- seofleet_cli-0.2.0/src/seofleet/checks/geo/link_header.py +116 -0
- seofleet_cli-0.2.0/src/seofleet/checks/geo/llms_txt.py +29 -0
- seofleet_cli-0.2.0/src/seofleet/checks/geo/markdown_negotiation.py +40 -0
- seofleet_cli-0.2.0/src/seofleet/checks/geo/organization_schema.py +89 -0
- seofleet_cli-0.2.0/src/seofleet/checks/geo/speakable_schema.py +66 -0
- seofleet_cli-0.2.0/src/seofleet/checks/geo/structured_data.py +53 -0
- seofleet_cli-0.2.0/src/seofleet/checks/technical/__init__.py +0 -0
- seofleet_cli-0.2.0/src/seofleet/checks/technical/canonical.py +48 -0
- seofleet_cli-0.2.0/src/seofleet/checks/technical/heading_structure.py +54 -0
- seofleet_cli-0.2.0/src/seofleet/checks/technical/image_alt.py +53 -0
- seofleet_cli-0.2.0/src/seofleet/checks/technical/image_weight.py +111 -0
- seofleet_cli-0.2.0/src/seofleet/checks/technical/meta_description.py +48 -0
- seofleet_cli-0.2.0/src/seofleet/checks/technical/open_graph.py +53 -0
- seofleet_cli-0.2.0/src/seofleet/checks/technical/redirect_chain.py +60 -0
- seofleet_cli-0.2.0/src/seofleet/checks/technical/robots_meta_directives.py +70 -0
- seofleet_cli-0.2.0/src/seofleet/checks/technical/robots_txt.py +39 -0
- seofleet_cli-0.2.0/src/seofleet/checks/technical/sitemap_xml.py +78 -0
- seofleet_cli-0.2.0/src/seofleet/checks/technical/title.py +52 -0
- seofleet_cli-0.2.0/src/seofleet/checks/technical/twitter_card.py +64 -0
- seofleet_cli-0.2.0/src/seofleet/cli.py +122 -0
- seofleet_cli-0.2.0/src/seofleet/cli_lib.py +106 -0
- seofleet_cli-0.2.0/src/seofleet/config.py +95 -0
- seofleet_cli-0.2.0/src/seofleet/errors.py +20 -0
- seofleet_cli-0.2.0/src/seofleet/fetch_utils.py +301 -0
- seofleet_cli-0.2.0/src/seofleet/fleet.py +152 -0
- seofleet_cli-0.2.0/src/seofleet/format.py +102 -0
- seofleet_cli-0.2.0/src/seofleet/html_util.py +162 -0
- seofleet_cli-0.2.0/src/seofleet/init.py +86 -0
- seofleet_cli-0.2.0/src/seofleet/py.typed +0 -0
- seofleet_cli-0.2.0/src/seofleet/report_file.py +49 -0
- seofleet_cli-0.2.0/src/seofleet/runner.py +35 -0
- seofleet_cli-0.2.0/src/seofleet/site_resources.py +119 -0
- seofleet_cli-0.2.0/src/seofleet/slugify.py +25 -0
- seofleet_cli-0.2.0/src/seofleet/types.py +84 -0
- seofleet_cli-0.2.0/tests/__init__.py +0 -0
- seofleet_cli-0.2.0/tests/conftest.py +143 -0
- seofleet_cli-0.2.0/tests/test_checks_geo.py +370 -0
- seofleet_cli-0.2.0/tests/test_checks_technical.py +500 -0
- seofleet_cli-0.2.0/tests/test_cli.py +230 -0
- seofleet_cli-0.2.0/tests/test_config.py +69 -0
- seofleet_cli-0.2.0/tests/test_e2e.py +60 -0
- seofleet_cli-0.2.0/tests/test_fetch_utils.py +267 -0
- seofleet_cli-0.2.0/tests/test_fleet.py +151 -0
- seofleet_cli-0.2.0/tests/test_format.py +61 -0
- seofleet_cli-0.2.0/tests/test_html_util.py +67 -0
- seofleet_cli-0.2.0/tests/test_init.py +49 -0
- seofleet_cli-0.2.0/tests/test_report_file.py +48 -0
- seofleet_cli-0.2.0/tests/test_runner.py +42 -0
- seofleet_cli-0.2.0/tests/test_site_resources.py +115 -0
- seofleet_cli-0.2.0/tests/test_slugify.py +25 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Rudrendu Paul
|
|
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.
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: seofleet-cli
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Zero-config, cross-platform SEO and GEO (generative engine optimization) checker: 12 checks against a live site, plus multi-site fleet mode, in pure Python with no extra runtime toolchain.
|
|
5
|
+
Project-URL: Homepage, https://github.com/RudrenduPaul/SeoFleet
|
|
6
|
+
Project-URL: Repository, https://github.com/RudrenduPaul/SeoFleet
|
|
7
|
+
Project-URL: Bug Tracker, https://github.com/RudrenduPaul/SeoFleet/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/RudrenduPaul/SeoFleet/blob/main/CHANGELOG.md
|
|
9
|
+
Project-URL: Documentation, https://github.com/RudrenduPaul/SeoFleet/blob/main/docs/getting-started.md
|
|
10
|
+
Project-URL: Author - Rudrendu Paul, https://github.com/RudrenduPaul
|
|
11
|
+
Project-URL: Author - Sourav Nandy, https://github.com/Sourav-nandy-ai
|
|
12
|
+
Author: Rudrendu Paul, Sourav Nandy
|
|
13
|
+
License-Expression: MIT
|
|
14
|
+
License-File: LICENSE
|
|
15
|
+
Keywords: agent-native,cli,developer-tools,generative-engine-optimization,geo,seo,website-audit
|
|
16
|
+
Classifier: Development Status :: 3 - Alpha
|
|
17
|
+
Classifier: Environment :: Console
|
|
18
|
+
Classifier: Intended Audience :: Developers
|
|
19
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
20
|
+
Classifier: Operating System :: OS Independent
|
|
21
|
+
Classifier: Programming Language :: Python :: 3
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
25
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
26
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
27
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: Site Management
|
|
28
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
29
|
+
Requires-Python: >=3.9
|
|
30
|
+
Provides-Extra: dev
|
|
31
|
+
Requires-Dist: build<2,>=1.0; extra == 'dev'
|
|
32
|
+
Requires-Dist: pytest<9,>=7.0; extra == 'dev'
|
|
33
|
+
Requires-Dist: twine<7,>=5.0; extra == 'dev'
|
|
34
|
+
Description-Content-Type: text/markdown
|
|
35
|
+
|
|
36
|
+
# seofleet-cli (Python)
|
|
37
|
+
|
|
38
|
+
Zero-config SEO and GEO (generative engine optimization) checker: 21 checks
|
|
39
|
+
against a live site, plus a multi-site fleet mode for agencies checking
|
|
40
|
+
several client sites at once, in pure Python with no extra runtime
|
|
41
|
+
toolchain -- no headless browser, no separate interpreter to provision.
|
|
42
|
+
|
|
43
|
+
[](https://pypi.org/project/seofleet-cli/)
|
|
44
|
+
[](https://github.com/RudrenduPaul/SeoFleet/blob/main/LICENSE)
|
|
45
|
+
[](https://pypi.org/project/seofleet-cli/)
|
|
46
|
+
[](https://github.com/RudrenduPaul/SeoFleet/actions/workflows/ci.yml)
|
|
47
|
+
[](https://www.npmjs.com/package/seofleet-cli)
|
|
48
|
+
|
|
49
|
+
## Why this exists
|
|
50
|
+
|
|
51
|
+
SeoFleet checks a website for 21 technical-SEO and GEO issues --
|
|
52
|
+
title/meta description length, canonical tags, robots.txt/sitemap.xml
|
|
53
|
+
reachability (with a robots.txt `Sitemap:` fallback), heading structure,
|
|
54
|
+
image alt coverage and weight, Open Graph and Twitter Card tags, meta
|
|
55
|
+
robots directives, redirect-chain health, JSON-LD structured data, llms.txt,
|
|
56
|
+
AI-crawler directives (training and search crawlers tracked separately --
|
|
57
|
+
GPTBot/OAI-SearchBot, ClaudeBot/Claude-SearchBot, PerplexityBot,
|
|
58
|
+
Google-Extended, Applebot-Extended), FAQ schema, Speakable schema,
|
|
59
|
+
Organization schema, Markdown content negotiation, an RFC 8288 Link header,
|
|
60
|
+
and content-extraction friendliness -- and reports PASS/WARN/FAIL with a
|
|
61
|
+
fix suggestion for anything short of a clean pass. This package is the
|
|
62
|
+
Python distribution of the tool: a genuine, independent port of the npm
|
|
63
|
+
CLI's logic, not a wrapper around the Node binary. It ships with **zero
|
|
64
|
+
runtime dependencies** -- HTML parsing and HTTP fetching both use only the
|
|
65
|
+
Python standard library.
|
|
66
|
+
|
|
67
|
+
## Install
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
pip install seofleet-cli
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
or with [uv](https://docs.astral.sh/uv/):
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
uv add seofleet-cli
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
The complementary JS/TS distribution installs from npm:
|
|
80
|
+
`npm install -g seofleet-cli` -- see the
|
|
81
|
+
[project README](https://github.com/RudrenduPaul/SeoFleet#readme) for that
|
|
82
|
+
package. Both packages read the same 21-check spec and are intended to
|
|
83
|
+
report the same PASS/WARN/FAIL verdicts for the same site; neither is
|
|
84
|
+
deprecated in favor of the other.
|
|
85
|
+
|
|
86
|
+
## Quickstart
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
seofleet init ./my-site --site-url https://example.com
|
|
90
|
+
seofleet check ./my-site
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
`init` scaffolds a `seofleet.json` config (and a Claude Code skill file) in
|
|
94
|
+
the target directory; `check` runs all 21 checks against the configured
|
|
95
|
+
`siteUrl` and prints a PASS/WARN/FAIL line per check plus a summary. Pass
|
|
96
|
+
`--json` before the subcommand for structured output an agent can parse:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
seofleet --json check ./my-site
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Or call the library directly (the agent-native path, no subprocess):
|
|
103
|
+
|
|
104
|
+
```python
|
|
105
|
+
from seofleet import load_site, run_checks, ALL_CHECKS
|
|
106
|
+
|
|
107
|
+
ctx = load_site("https://example.com")
|
|
108
|
+
results = run_checks(ALL_CHECKS, ctx)
|
|
109
|
+
for r in results:
|
|
110
|
+
print(f"[{r.status}] ({r.category}) {r.name}: {r.message}")
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## How it works
|
|
114
|
+
|
|
115
|
+
```
|
|
116
|
+
seofleet.json -> site URL -> fetch homepage + robots.txt + sitemap.xml + llms.txt (parallel)
|
|
117
|
+
-> 21 checks (12 technical + 9 GEO) run against the shared fetched context
|
|
118
|
+
-> PASS / WARN / FAIL per check -> exit code (0 clean / 1 any FAIL / 2 usage error)
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Full data model, the 21 checks explained, and fleet-mode semantics are in
|
|
122
|
+
[docs/concepts.md](https://github.com/RudrenduPaul/SeoFleet/blob/main/docs/concepts.md)
|
|
123
|
+
and [docs/getting-started.md](https://github.com/RudrenduPaul/SeoFleet/blob/main/docs/getting-started.md).
|
|
124
|
+
The checks are reimplemented as genuine Python logic against the same
|
|
125
|
+
check contract the npm package uses (`Check.run(ctx) -> CheckResult`) --
|
|
126
|
+
see those docs for what each check actually verifies.
|
|
127
|
+
|
|
128
|
+
## Fleet mode
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
seofleet fleet ./fleet.json
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Runs the full 21-check suite against every site declared in a local JSON
|
|
135
|
+
manifest (`{"sites": [{"name": ..., "path": ...}]}`) in one invocation --
|
|
136
|
+
local filesystem only, no SSH, no remote execution. Aimed at agencies or
|
|
137
|
+
teams checking several client repos side by side. Add `--out-dir ./reports`
|
|
138
|
+
to also write one auto-named report file per site, named from the
|
|
139
|
+
manifest's `name` field.
|
|
140
|
+
|
|
141
|
+
## CI integration
|
|
142
|
+
|
|
143
|
+
```yaml
|
|
144
|
+
- uses: actions/checkout@v4
|
|
145
|
+
- uses: actions/setup-python@v5
|
|
146
|
+
with:
|
|
147
|
+
python-version: '3.12'
|
|
148
|
+
- run: pip install seofleet-cli
|
|
149
|
+
- run: seofleet check ./my-site --json > results.json
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Full walkthrough in
|
|
153
|
+
[docs/integrations/ci.md](https://github.com/RudrenduPaul/SeoFleet/blob/main/docs/integrations/ci.md).
|
|
154
|
+
|
|
155
|
+
## Security
|
|
156
|
+
|
|
157
|
+
seofleet's fetch layer only ever dials `http(s)`, follows redirects
|
|
158
|
+
manually one hop at a time, refuses to follow a redirect whose `Location`
|
|
159
|
+
targets a non-`http(s)` scheme, and bounds the redirect chain at 5 hops so
|
|
160
|
+
a redirect loop can't hang the process -- see
|
|
161
|
+
[SECURITY.md](https://github.com/RudrenduPaul/SeoFleet/blob/main/SECURITY.md)
|
|
162
|
+
for the full policy and how to report a vulnerability. **Honest note**:
|
|
163
|
+
this project does not currently publish SLSA provenance, Sigstore
|
|
164
|
+
signatures, or an SBOM, and has no OpenSSF Scorecard badge set up -- none
|
|
165
|
+
of that infrastructure exists yet for either distribution, so it isn't
|
|
166
|
+
claimed here.
|
|
167
|
+
|
|
168
|
+
## Contributing
|
|
169
|
+
|
|
170
|
+
See [CONTRIBUTING.md](https://github.com/RudrenduPaul/SeoFleet/blob/main/CONTRIBUTING.md).
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
cd python
|
|
174
|
+
python3 -m venv .venv && source .venv/bin/activate
|
|
175
|
+
pip install -e ".[dev]"
|
|
176
|
+
pytest
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## License
|
|
180
|
+
|
|
181
|
+
MIT, see [LICENSE](https://github.com/RudrenduPaul/SeoFleet/blob/main/LICENSE).
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# seofleet-cli (Python)
|
|
2
|
+
|
|
3
|
+
Zero-config SEO and GEO (generative engine optimization) checker: 21 checks
|
|
4
|
+
against a live site, plus a multi-site fleet mode for agencies checking
|
|
5
|
+
several client sites at once, in pure Python with no extra runtime
|
|
6
|
+
toolchain -- no headless browser, no separate interpreter to provision.
|
|
7
|
+
|
|
8
|
+
[](https://pypi.org/project/seofleet-cli/)
|
|
9
|
+
[](https://github.com/RudrenduPaul/SeoFleet/blob/main/LICENSE)
|
|
10
|
+
[](https://pypi.org/project/seofleet-cli/)
|
|
11
|
+
[](https://github.com/RudrenduPaul/SeoFleet/actions/workflows/ci.yml)
|
|
12
|
+
[](https://www.npmjs.com/package/seofleet-cli)
|
|
13
|
+
|
|
14
|
+
## Why this exists
|
|
15
|
+
|
|
16
|
+
SeoFleet checks a website for 21 technical-SEO and GEO issues --
|
|
17
|
+
title/meta description length, canonical tags, robots.txt/sitemap.xml
|
|
18
|
+
reachability (with a robots.txt `Sitemap:` fallback), heading structure,
|
|
19
|
+
image alt coverage and weight, Open Graph and Twitter Card tags, meta
|
|
20
|
+
robots directives, redirect-chain health, JSON-LD structured data, llms.txt,
|
|
21
|
+
AI-crawler directives (training and search crawlers tracked separately --
|
|
22
|
+
GPTBot/OAI-SearchBot, ClaudeBot/Claude-SearchBot, PerplexityBot,
|
|
23
|
+
Google-Extended, Applebot-Extended), FAQ schema, Speakable schema,
|
|
24
|
+
Organization schema, Markdown content negotiation, an RFC 8288 Link header,
|
|
25
|
+
and content-extraction friendliness -- and reports PASS/WARN/FAIL with a
|
|
26
|
+
fix suggestion for anything short of a clean pass. This package is the
|
|
27
|
+
Python distribution of the tool: a genuine, independent port of the npm
|
|
28
|
+
CLI's logic, not a wrapper around the Node binary. It ships with **zero
|
|
29
|
+
runtime dependencies** -- HTML parsing and HTTP fetching both use only the
|
|
30
|
+
Python standard library.
|
|
31
|
+
|
|
32
|
+
## Install
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pip install seofleet-cli
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
or with [uv](https://docs.astral.sh/uv/):
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
uv add seofleet-cli
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
The complementary JS/TS distribution installs from npm:
|
|
45
|
+
`npm install -g seofleet-cli` -- see the
|
|
46
|
+
[project README](https://github.com/RudrenduPaul/SeoFleet#readme) for that
|
|
47
|
+
package. Both packages read the same 21-check spec and are intended to
|
|
48
|
+
report the same PASS/WARN/FAIL verdicts for the same site; neither is
|
|
49
|
+
deprecated in favor of the other.
|
|
50
|
+
|
|
51
|
+
## Quickstart
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
seofleet init ./my-site --site-url https://example.com
|
|
55
|
+
seofleet check ./my-site
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
`init` scaffolds a `seofleet.json` config (and a Claude Code skill file) in
|
|
59
|
+
the target directory; `check` runs all 21 checks against the configured
|
|
60
|
+
`siteUrl` and prints a PASS/WARN/FAIL line per check plus a summary. Pass
|
|
61
|
+
`--json` before the subcommand for structured output an agent can parse:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
seofleet --json check ./my-site
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Or call the library directly (the agent-native path, no subprocess):
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
from seofleet import load_site, run_checks, ALL_CHECKS
|
|
71
|
+
|
|
72
|
+
ctx = load_site("https://example.com")
|
|
73
|
+
results = run_checks(ALL_CHECKS, ctx)
|
|
74
|
+
for r in results:
|
|
75
|
+
print(f"[{r.status}] ({r.category}) {r.name}: {r.message}")
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## How it works
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
seofleet.json -> site URL -> fetch homepage + robots.txt + sitemap.xml + llms.txt (parallel)
|
|
82
|
+
-> 21 checks (12 technical + 9 GEO) run against the shared fetched context
|
|
83
|
+
-> PASS / WARN / FAIL per check -> exit code (0 clean / 1 any FAIL / 2 usage error)
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Full data model, the 21 checks explained, and fleet-mode semantics are in
|
|
87
|
+
[docs/concepts.md](https://github.com/RudrenduPaul/SeoFleet/blob/main/docs/concepts.md)
|
|
88
|
+
and [docs/getting-started.md](https://github.com/RudrenduPaul/SeoFleet/blob/main/docs/getting-started.md).
|
|
89
|
+
The checks are reimplemented as genuine Python logic against the same
|
|
90
|
+
check contract the npm package uses (`Check.run(ctx) -> CheckResult`) --
|
|
91
|
+
see those docs for what each check actually verifies.
|
|
92
|
+
|
|
93
|
+
## Fleet mode
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
seofleet fleet ./fleet.json
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Runs the full 21-check suite against every site declared in a local JSON
|
|
100
|
+
manifest (`{"sites": [{"name": ..., "path": ...}]}`) in one invocation --
|
|
101
|
+
local filesystem only, no SSH, no remote execution. Aimed at agencies or
|
|
102
|
+
teams checking several client repos side by side. Add `--out-dir ./reports`
|
|
103
|
+
to also write one auto-named report file per site, named from the
|
|
104
|
+
manifest's `name` field.
|
|
105
|
+
|
|
106
|
+
## CI integration
|
|
107
|
+
|
|
108
|
+
```yaml
|
|
109
|
+
- uses: actions/checkout@v4
|
|
110
|
+
- uses: actions/setup-python@v5
|
|
111
|
+
with:
|
|
112
|
+
python-version: '3.12'
|
|
113
|
+
- run: pip install seofleet-cli
|
|
114
|
+
- run: seofleet check ./my-site --json > results.json
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Full walkthrough in
|
|
118
|
+
[docs/integrations/ci.md](https://github.com/RudrenduPaul/SeoFleet/blob/main/docs/integrations/ci.md).
|
|
119
|
+
|
|
120
|
+
## Security
|
|
121
|
+
|
|
122
|
+
seofleet's fetch layer only ever dials `http(s)`, follows redirects
|
|
123
|
+
manually one hop at a time, refuses to follow a redirect whose `Location`
|
|
124
|
+
targets a non-`http(s)` scheme, and bounds the redirect chain at 5 hops so
|
|
125
|
+
a redirect loop can't hang the process -- see
|
|
126
|
+
[SECURITY.md](https://github.com/RudrenduPaul/SeoFleet/blob/main/SECURITY.md)
|
|
127
|
+
for the full policy and how to report a vulnerability. **Honest note**:
|
|
128
|
+
this project does not currently publish SLSA provenance, Sigstore
|
|
129
|
+
signatures, or an SBOM, and has no OpenSSF Scorecard badge set up -- none
|
|
130
|
+
of that infrastructure exists yet for either distribution, so it isn't
|
|
131
|
+
claimed here.
|
|
132
|
+
|
|
133
|
+
## Contributing
|
|
134
|
+
|
|
135
|
+
See [CONTRIBUTING.md](https://github.com/RudrenduPaul/SeoFleet/blob/main/CONTRIBUTING.md).
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
cd python
|
|
139
|
+
python3 -m venv .venv && source .venv/bin/activate
|
|
140
|
+
pip install -e ".[dev]"
|
|
141
|
+
pytest
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## License
|
|
145
|
+
|
|
146
|
+
MIT, see [LICENSE](https://github.com/RudrenduPaul/SeoFleet/blob/main/LICENSE).
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "seofleet-cli"
|
|
7
|
+
version = "0.2.0"
|
|
8
|
+
description = "Zero-config, cross-platform SEO and GEO (generative engine optimization) checker: 12 checks against a live site, plus multi-site fleet mode, in pure Python with no extra runtime toolchain."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "Rudrendu Paul" },
|
|
14
|
+
{ name = "Sourav Nandy" },
|
|
15
|
+
]
|
|
16
|
+
keywords = ["seo", "geo", "generative-engine-optimization", "cli", "agent-native", "website-audit", "developer-tools"]
|
|
17
|
+
classifiers = [
|
|
18
|
+
"Development Status :: 3 - Alpha",
|
|
19
|
+
"Environment :: Console",
|
|
20
|
+
"Intended Audience :: Developers",
|
|
21
|
+
"License :: OSI Approved :: MIT License",
|
|
22
|
+
"Operating System :: OS Independent",
|
|
23
|
+
"Programming Language :: Python :: 3",
|
|
24
|
+
"Programming Language :: Python :: 3.9",
|
|
25
|
+
"Programming Language :: Python :: 3.10",
|
|
26
|
+
"Programming Language :: Python :: 3.11",
|
|
27
|
+
"Programming Language :: Python :: 3.12",
|
|
28
|
+
"Programming Language :: Python :: 3.13",
|
|
29
|
+
"Topic :: Internet :: WWW/HTTP :: Site Management",
|
|
30
|
+
"Topic :: Software Development :: Quality Assurance",
|
|
31
|
+
]
|
|
32
|
+
dependencies = []
|
|
33
|
+
|
|
34
|
+
[project.optional-dependencies]
|
|
35
|
+
dev = [
|
|
36
|
+
"pytest>=7.0,<9",
|
|
37
|
+
"build>=1.0,<2",
|
|
38
|
+
"twine>=5.0,<7",
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
[project.urls]
|
|
42
|
+
Homepage = "https://github.com/RudrenduPaul/SeoFleet"
|
|
43
|
+
Repository = "https://github.com/RudrenduPaul/SeoFleet"
|
|
44
|
+
"Bug Tracker" = "https://github.com/RudrenduPaul/SeoFleet/issues"
|
|
45
|
+
Changelog = "https://github.com/RudrenduPaul/SeoFleet/blob/main/CHANGELOG.md"
|
|
46
|
+
Documentation = "https://github.com/RudrenduPaul/SeoFleet/blob/main/docs/getting-started.md"
|
|
47
|
+
"Author - Rudrendu Paul" = "https://github.com/RudrenduPaul"
|
|
48
|
+
"Author - Sourav Nandy" = "https://github.com/Sourav-nandy-ai"
|
|
49
|
+
|
|
50
|
+
[project.scripts]
|
|
51
|
+
seofleet = "seofleet.cli:main"
|
|
52
|
+
|
|
53
|
+
[tool.hatch.build.targets.wheel]
|
|
54
|
+
packages = ["src/seofleet"]
|
|
55
|
+
|
|
56
|
+
[tool.hatch.build.targets.sdist]
|
|
57
|
+
include = [
|
|
58
|
+
"src/seofleet",
|
|
59
|
+
"tests",
|
|
60
|
+
"README.md",
|
|
61
|
+
"LICENSE",
|
|
62
|
+
]
|
|
63
|
+
exclude = [
|
|
64
|
+
".gitignore",
|
|
65
|
+
"examples",
|
|
66
|
+
]
|
|
67
|
+
|
|
68
|
+
[tool.pytest.ini_options]
|
|
69
|
+
testpaths = ["tests"]
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Programmatic / agent-native entry point.
|
|
3
|
+
|
|
4
|
+
from seofleet import load_site, run_checks, select_checks, ALL_CHECKS
|
|
5
|
+
|
|
6
|
+
ctx = load_site("https://example.com")
|
|
7
|
+
results = run_checks(ALL_CHECKS, ctx)
|
|
8
|
+
for r in results:
|
|
9
|
+
print(r.status, r.name, r.message)
|
|
10
|
+
|
|
11
|
+
This is the Python port of the seofleet-cli npm package
|
|
12
|
+
(https://www.npmjs.com/package/seofleet-cli). Both distributions run the
|
|
13
|
+
same 12 technical-SEO/GEO checks; see
|
|
14
|
+
https://github.com/RudrenduPaul/SeoFleet for the canonical documentation,
|
|
15
|
+
comparison table, and the original TypeScript source.
|
|
16
|
+
"""
|
|
17
|
+
from .checks import (
|
|
18
|
+
ALL_CHECKS,
|
|
19
|
+
GEO_CHECKS,
|
|
20
|
+
TECHNICAL_CHECKS,
|
|
21
|
+
ai_crawler_directives_check,
|
|
22
|
+
canonical_check,
|
|
23
|
+
content_extraction_check,
|
|
24
|
+
faq_schema_check,
|
|
25
|
+
heading_structure_check,
|
|
26
|
+
image_alt_check,
|
|
27
|
+
llms_txt_check,
|
|
28
|
+
meta_description_check,
|
|
29
|
+
robots_txt_check,
|
|
30
|
+
sitemap_xml_check,
|
|
31
|
+
structured_data_check,
|
|
32
|
+
title_check,
|
|
33
|
+
)
|
|
34
|
+
from .config import CONFIG_FILENAME, SeoFleetConfig, default_config, load_config, select_checks
|
|
35
|
+
from .errors import SeoFleetError
|
|
36
|
+
from .fetch_utils import FetchedResource, assert_http_url, safe_fetch
|
|
37
|
+
from .fleet import FleetManifest, FleetManifestEntry, FleetSiteResult, load_fleet_manifest, run_fleet
|
|
38
|
+
from .format import (
|
|
39
|
+
format_check_results_json,
|
|
40
|
+
format_check_results_text,
|
|
41
|
+
format_fleet_results_json,
|
|
42
|
+
format_fleet_results_text,
|
|
43
|
+
format_init_result_json,
|
|
44
|
+
format_init_result_text,
|
|
45
|
+
)
|
|
46
|
+
from .init import InitResult, init_project
|
|
47
|
+
from .runner import has_failure, run_checks
|
|
48
|
+
from .site_resources import build_check_context, fetch_site_resources, load_site
|
|
49
|
+
from .types import Check, CheckContext, CheckResult, SiteResources
|
|
50
|
+
|
|
51
|
+
__version__ = "0.2.0"
|
|
52
|
+
|
|
53
|
+
__all__ = [
|
|
54
|
+
"__version__",
|
|
55
|
+
# Types
|
|
56
|
+
"Check",
|
|
57
|
+
"CheckContext",
|
|
58
|
+
"CheckResult",
|
|
59
|
+
"SiteResources",
|
|
60
|
+
"FetchedResource",
|
|
61
|
+
"SeoFleetError",
|
|
62
|
+
"SeoFleetConfig",
|
|
63
|
+
"InitResult",
|
|
64
|
+
"FleetManifest",
|
|
65
|
+
"FleetManifestEntry",
|
|
66
|
+
"FleetSiteResult",
|
|
67
|
+
# Checks
|
|
68
|
+
"ALL_CHECKS",
|
|
69
|
+
"TECHNICAL_CHECKS",
|
|
70
|
+
"GEO_CHECKS",
|
|
71
|
+
"title_check",
|
|
72
|
+
"meta_description_check",
|
|
73
|
+
"canonical_check",
|
|
74
|
+
"robots_txt_check",
|
|
75
|
+
"sitemap_xml_check",
|
|
76
|
+
"heading_structure_check",
|
|
77
|
+
"image_alt_check",
|
|
78
|
+
"structured_data_check",
|
|
79
|
+
"llms_txt_check",
|
|
80
|
+
"ai_crawler_directives_check",
|
|
81
|
+
"faq_schema_check",
|
|
82
|
+
"content_extraction_check",
|
|
83
|
+
# Core functions
|
|
84
|
+
"run_checks",
|
|
85
|
+
"has_failure",
|
|
86
|
+
"fetch_site_resources",
|
|
87
|
+
"build_check_context",
|
|
88
|
+
"load_site",
|
|
89
|
+
"safe_fetch",
|
|
90
|
+
"assert_http_url",
|
|
91
|
+
"load_config",
|
|
92
|
+
"default_config",
|
|
93
|
+
"select_checks",
|
|
94
|
+
"CONFIG_FILENAME",
|
|
95
|
+
"init_project",
|
|
96
|
+
"load_fleet_manifest",
|
|
97
|
+
"run_fleet",
|
|
98
|
+
"format_check_results_text",
|
|
99
|
+
"format_check_results_json",
|
|
100
|
+
"format_fleet_results_text",
|
|
101
|
+
"format_fleet_results_json",
|
|
102
|
+
"format_init_result_text",
|
|
103
|
+
"format_init_result_json",
|
|
104
|
+
]
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"""Ported from src/checks/index.ts."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from typing import List
|
|
5
|
+
|
|
6
|
+
from ..types import Check
|
|
7
|
+
from .geo.ai_crawler_directives import ai_crawler_directives_check
|
|
8
|
+
from .geo.content_extraction import content_extraction_check
|
|
9
|
+
from .geo.faq_schema import faq_schema_check
|
|
10
|
+
from .geo.link_header import link_header_check
|
|
11
|
+
from .geo.llms_txt import llms_txt_check
|
|
12
|
+
from .geo.markdown_negotiation import markdown_negotiation_check
|
|
13
|
+
from .geo.organization_schema import organization_schema_check
|
|
14
|
+
from .geo.speakable_schema import speakable_schema_check
|
|
15
|
+
from .geo.structured_data import structured_data_check
|
|
16
|
+
from .technical.canonical import canonical_check
|
|
17
|
+
from .technical.heading_structure import heading_structure_check
|
|
18
|
+
from .technical.image_alt import image_alt_check
|
|
19
|
+
from .technical.image_weight import image_weight_check
|
|
20
|
+
from .technical.meta_description import meta_description_check
|
|
21
|
+
from .technical.open_graph import open_graph_check
|
|
22
|
+
from .technical.robots_meta_directives import robots_meta_directives_check
|
|
23
|
+
from .technical.redirect_chain import redirect_chain_check
|
|
24
|
+
from .technical.robots_txt import robots_txt_check
|
|
25
|
+
from .technical.sitemap_xml import sitemap_xml_check
|
|
26
|
+
from .technical.title import title_check
|
|
27
|
+
from .technical.twitter_card import twitter_card_check
|
|
28
|
+
|
|
29
|
+
TECHNICAL_CHECKS: List[Check] = [
|
|
30
|
+
title_check,
|
|
31
|
+
meta_description_check,
|
|
32
|
+
canonical_check,
|
|
33
|
+
robots_txt_check,
|
|
34
|
+
sitemap_xml_check,
|
|
35
|
+
heading_structure_check,
|
|
36
|
+
image_alt_check,
|
|
37
|
+
open_graph_check,
|
|
38
|
+
twitter_card_check,
|
|
39
|
+
robots_meta_directives_check,
|
|
40
|
+
image_weight_check,
|
|
41
|
+
redirect_chain_check,
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
GEO_CHECKS: List[Check] = [
|
|
45
|
+
structured_data_check,
|
|
46
|
+
llms_txt_check,
|
|
47
|
+
ai_crawler_directives_check,
|
|
48
|
+
faq_schema_check,
|
|
49
|
+
content_extraction_check,
|
|
50
|
+
speakable_schema_check,
|
|
51
|
+
organization_schema_check,
|
|
52
|
+
markdown_negotiation_check,
|
|
53
|
+
link_header_check,
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
ALL_CHECKS: List[Check] = [*TECHNICAL_CHECKS, *GEO_CHECKS]
|
|
57
|
+
|
|
58
|
+
__all__ = [
|
|
59
|
+
"TECHNICAL_CHECKS",
|
|
60
|
+
"GEO_CHECKS",
|
|
61
|
+
"ALL_CHECKS",
|
|
62
|
+
"title_check",
|
|
63
|
+
"meta_description_check",
|
|
64
|
+
"canonical_check",
|
|
65
|
+
"robots_txt_check",
|
|
66
|
+
"sitemap_xml_check",
|
|
67
|
+
"heading_structure_check",
|
|
68
|
+
"image_alt_check",
|
|
69
|
+
"open_graph_check",
|
|
70
|
+
"twitter_card_check",
|
|
71
|
+
"robots_meta_directives_check",
|
|
72
|
+
"image_weight_check",
|
|
73
|
+
"redirect_chain_check",
|
|
74
|
+
"structured_data_check",
|
|
75
|
+
"llms_txt_check",
|
|
76
|
+
"ai_crawler_directives_check",
|
|
77
|
+
"faq_schema_check",
|
|
78
|
+
"content_extraction_check",
|
|
79
|
+
"speakable_schema_check",
|
|
80
|
+
"organization_schema_check",
|
|
81
|
+
"markdown_negotiation_check",
|
|
82
|
+
"link_header_check",
|
|
83
|
+
]
|
|
File without changes
|