bmad-plus 0.3.3 → 0.4.1
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.
- package/CHANGELOG.md +34 -0
- package/README.md +12 -56
- package/osint-agent-package/skills/bmad-osint-investigate/osint/SKILL.md +452 -452
- package/osint-agent-package/skills/bmad-osint-investigate/osint/assets/dossier-template.md +116 -116
- package/osint-agent-package/skills/bmad-osint-investigate/osint/references/content-extraction.md +100 -100
- package/osint-agent-package/skills/bmad-osint-investigate/osint/references/platforms.md +130 -130
- package/osint-agent-package/skills/bmad-osint-investigate/osint/references/psychoprofile.md +69 -69
- package/osint-agent-package/skills/bmad-osint-investigate/osint/references/tools.md +281 -281
- package/osint-agent-package/skills/bmad-osint-investigate/osint/scripts/mcp-client.py +136 -136
- package/package.json +1 -1
- package/readme-international/README.de.md +1 -1
- package/readme-international/README.es.md +1 -1
- package/readme-international/README.fr.md +1 -1
- package/tools/cli/commands/install.js +74 -46
- package/tools/cli/i18n.js +501 -0
- package/oveanet-pack/animated-website/DEPLOYMENT.md +0 -104
- package/oveanet-pack/animated-website/README.md +0 -63
- package/oveanet-pack/animated-website/agent/animated-website-agent.md +0 -325
- package/oveanet-pack/animated-website/agent.yaml +0 -63
- package/oveanet-pack/animated-website/templates/animated-website-workflow.md +0 -55
- package/oveanet-pack/seo-audit-360/DEPLOYMENT.md +0 -115
- package/oveanet-pack/seo-audit-360/README.md +0 -66
- package/oveanet-pack/seo-audit-360/SKILL.md +0 -171
- package/oveanet-pack/seo-audit-360/agent/seo-chief.md +0 -294
- package/oveanet-pack/seo-audit-360/agent/seo-judge.md +0 -241
- package/oveanet-pack/seo-audit-360/agent/seo-scout.md +0 -171
- package/oveanet-pack/seo-audit-360/agent.yaml +0 -70
- package/oveanet-pack/seo-audit-360/checklist.md +0 -140
- package/oveanet-pack/seo-audit-360/hooks/seo-check.sh +0 -95
- package/oveanet-pack/seo-audit-360/pagespeed-playbook.md +0 -320
- package/oveanet-pack/seo-audit-360/ref/audit-schema.json +0 -187
- package/oveanet-pack/seo-audit-360/ref/cwv-thresholds.md +0 -87
- package/oveanet-pack/seo-audit-360/ref/eeat-criteria.md +0 -123
- package/oveanet-pack/seo-audit-360/ref/geo-signals.md +0 -167
- package/oveanet-pack/seo-audit-360/ref/hreflang-rules.md +0 -153
- package/oveanet-pack/seo-audit-360/ref/quality-gates.md +0 -133
- package/oveanet-pack/seo-audit-360/ref/schema-catalog.md +0 -91
- package/oveanet-pack/seo-audit-360/ref/schema-templates.json +0 -356
- package/oveanet-pack/seo-audit-360/requirements.txt +0 -14
- package/oveanet-pack/seo-audit-360/scripts/__pycache__/seo_crawl.cpython-314.pyc +0 -0
- package/oveanet-pack/seo-audit-360/scripts/__pycache__/seo_parse.cpython-314.pyc +0 -0
- package/oveanet-pack/seo-audit-360/scripts/install.ps1 +0 -53
- package/oveanet-pack/seo-audit-360/scripts/install.sh +0 -48
- package/oveanet-pack/seo-audit-360/scripts/seo_apis.py +0 -464
- package/oveanet-pack/seo-audit-360/scripts/seo_crawl.py +0 -282
- package/oveanet-pack/seo-audit-360/scripts/seo_fetch.py +0 -231
- package/oveanet-pack/seo-audit-360/scripts/seo_parse.py +0 -255
- package/oveanet-pack/seo-audit-360/scripts/seo_report.py +0 -403
- package/oveanet-pack/seo-audit-360/scripts/seo_screenshot.py +0 -202
- package/oveanet-pack/seo-audit-360/templates/seo-audit-workflow.md +0 -241
- package/oveanet-pack/seo-audit-360/tests/__pycache__/test_crawl.cpython-314-pytest-9.0.2.pyc +0 -0
- package/oveanet-pack/seo-audit-360/tests/__pycache__/test_parse.cpython-314-pytest-9.0.2.pyc +0 -0
- package/oveanet-pack/seo-audit-360/tests/fixtures/sample_page.html +0 -62
- package/oveanet-pack/seo-audit-360/tests/test_apis.py +0 -75
- package/oveanet-pack/seo-audit-360/tests/test_crawl.py +0 -121
- package/oveanet-pack/seo-audit-360/tests/test_fetch.py +0 -70
- package/oveanet-pack/seo-audit-360/tests/test_parse.py +0 -184
- package/oveanet-pack/universal-backup/DEPLOYMENT.md +0 -80
- package/oveanet-pack/universal-backup/README.md +0 -58
- package/oveanet-pack/universal-backup/agent/backup-agent.md +0 -71
- package/oveanet-pack/universal-backup/agent.yaml +0 -45
- package/oveanet-pack/universal-backup/templates/backup-workflow.md +0 -51
|
@@ -1,202 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
3
|
-
SEO Screenshot — Viewport screenshot capture for visual SEO analysis.
|
|
4
|
-
|
|
5
|
-
Features:
|
|
6
|
-
- Mobile and desktop viewport presets
|
|
7
|
-
- Above-the-fold element detection
|
|
8
|
-
- Full-page capture option
|
|
9
|
-
- PNG output with configurable quality
|
|
10
|
-
|
|
11
|
-
Requires: playwright (pip install playwright && playwright install chromium)
|
|
12
|
-
|
|
13
|
-
Author: Laurent Rochetta
|
|
14
|
-
License: MIT
|
|
15
|
-
"""
|
|
16
|
-
|
|
17
|
-
import argparse
|
|
18
|
-
import sys
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
VIEWPORTS = {
|
|
22
|
-
"mobile": {"width": 375, "height": 812, "device_scale_factor": 3, "is_mobile": True},
|
|
23
|
-
"tablet": {"width": 768, "height": 1024, "device_scale_factor": 2, "is_mobile": True},
|
|
24
|
-
"desktop": {"width": 1440, "height": 900, "device_scale_factor": 1, "is_mobile": False},
|
|
25
|
-
"desktop-hd": {"width": 1920, "height": 1080, "device_scale_factor": 1, "is_mobile": False},
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
def capture_screenshot(
|
|
30
|
-
url: str,
|
|
31
|
-
output: str = "screenshot.png",
|
|
32
|
-
viewport: str = "desktop",
|
|
33
|
-
full_page: bool = False,
|
|
34
|
-
wait_ms: int = 2000,
|
|
35
|
-
):
|
|
36
|
-
"""
|
|
37
|
-
Capture a viewport screenshot of a URL using Playwright.
|
|
38
|
-
|
|
39
|
-
Args:
|
|
40
|
-
url: URL to capture
|
|
41
|
-
output: Output file path (.png)
|
|
42
|
-
viewport: Viewport preset (mobile, tablet, desktop, desktop-hd)
|
|
43
|
-
full_page: Capture full page scroll or just viewport
|
|
44
|
-
wait_ms: Wait time after page load (ms)
|
|
45
|
-
"""
|
|
46
|
-
try:
|
|
47
|
-
from playwright.sync_api import sync_playwright
|
|
48
|
-
except ImportError:
|
|
49
|
-
print(
|
|
50
|
-
"Error: playwright required.\n"
|
|
51
|
-
"Install: pip install playwright && playwright install chromium",
|
|
52
|
-
file=sys.stderr,
|
|
53
|
-
)
|
|
54
|
-
sys.exit(1)
|
|
55
|
-
|
|
56
|
-
vp = VIEWPORTS.get(viewport, VIEWPORTS["desktop"])
|
|
57
|
-
|
|
58
|
-
with sync_playwright() as p:
|
|
59
|
-
browser = p.chromium.launch(headless=True)
|
|
60
|
-
context = browser.new_context(
|
|
61
|
-
viewport={"width": vp["width"], "height": vp["height"]},
|
|
62
|
-
device_scale_factor=vp["device_scale_factor"],
|
|
63
|
-
is_mobile=vp["is_mobile"],
|
|
64
|
-
user_agent=(
|
|
65
|
-
"Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) "
|
|
66
|
-
"AppleWebKit/605.1.15 Mobile/15E148 Safari/604.1"
|
|
67
|
-
if vp["is_mobile"]
|
|
68
|
-
else "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 "
|
|
69
|
-
"(KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 BMADSEOEngine/2.0"
|
|
70
|
-
),
|
|
71
|
-
)
|
|
72
|
-
|
|
73
|
-
page = context.new_page()
|
|
74
|
-
|
|
75
|
-
try:
|
|
76
|
-
page.goto(url, wait_until="networkidle", timeout=30000)
|
|
77
|
-
except Exception:
|
|
78
|
-
# Fallback: wait for load event instead
|
|
79
|
-
page.goto(url, wait_until="load", timeout=30000)
|
|
80
|
-
|
|
81
|
-
# Wait for dynamic content
|
|
82
|
-
page.wait_for_timeout(wait_ms)
|
|
83
|
-
|
|
84
|
-
# Capture screenshot
|
|
85
|
-
page.screenshot(path=output, full_page=full_page)
|
|
86
|
-
|
|
87
|
-
# Gather above-the-fold metrics
|
|
88
|
-
metrics = page.evaluate("""() => {
|
|
89
|
-
const viewportHeight = window.innerHeight;
|
|
90
|
-
const viewportWidth = window.innerWidth;
|
|
91
|
-
|
|
92
|
-
// Find CTAs above the fold
|
|
93
|
-
const ctas = [];
|
|
94
|
-
const buttons = document.querySelectorAll('a, button, [role="button"]');
|
|
95
|
-
buttons.forEach(el => {
|
|
96
|
-
const rect = el.getBoundingClientRect();
|
|
97
|
-
if (rect.top < viewportHeight && rect.bottom > 0) {
|
|
98
|
-
const text = el.textContent.trim().substring(0, 50);
|
|
99
|
-
if (text && (
|
|
100
|
-
/sign.?up|get.?start|try|buy|contact|demo|free|download|subscribe/i.test(text)
|
|
101
|
-
)) {
|
|
102
|
-
ctas.push({
|
|
103
|
-
text: text,
|
|
104
|
-
tag: el.tagName,
|
|
105
|
-
top: Math.round(rect.top),
|
|
106
|
-
visible: rect.width > 0 && rect.height > 0,
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
// Find hero/LCP candidate
|
|
113
|
-
const images = document.querySelectorAll('img');
|
|
114
|
-
let largestImage = null;
|
|
115
|
-
let largestArea = 0;
|
|
116
|
-
images.forEach(img => {
|
|
117
|
-
const rect = img.getBoundingClientRect();
|
|
118
|
-
const area = rect.width * rect.height;
|
|
119
|
-
if (area > largestArea && rect.top < viewportHeight) {
|
|
120
|
-
largestArea = area;
|
|
121
|
-
largestImage = {
|
|
122
|
-
src: img.src.substring(0, 100),
|
|
123
|
-
width: Math.round(rect.width),
|
|
124
|
-
height: Math.round(rect.height),
|
|
125
|
-
top: Math.round(rect.top),
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
// Check for horizontal scroll
|
|
131
|
-
const hasHorizontalScroll = document.documentElement.scrollWidth > viewportWidth;
|
|
132
|
-
|
|
133
|
-
// Font size check
|
|
134
|
-
const body = document.body;
|
|
135
|
-
const bodyFontSize = body ? parseFloat(getComputedStyle(body).fontSize) : 16;
|
|
136
|
-
|
|
137
|
-
return {
|
|
138
|
-
viewportWidth,
|
|
139
|
-
viewportHeight,
|
|
140
|
-
ctas_above_fold: ctas.length,
|
|
141
|
-
cta_details: ctas.slice(0, 5),
|
|
142
|
-
largest_image_above_fold: largestImage,
|
|
143
|
-
has_horizontal_scroll: hasHorizontalScroll,
|
|
144
|
-
body_font_size_px: bodyFontSize,
|
|
145
|
-
dom_element_count: document.querySelectorAll('*').length,
|
|
146
|
-
};
|
|
147
|
-
}""")
|
|
148
|
-
|
|
149
|
-
browser.close()
|
|
150
|
-
|
|
151
|
-
return metrics
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
# ── CLI ────────────────────────────────────────────────────────────
|
|
155
|
-
|
|
156
|
-
def main():
|
|
157
|
-
parser = argparse.ArgumentParser(
|
|
158
|
-
description="SEO Screenshot — Viewport capture (BMAD+ SEO Engine)"
|
|
159
|
-
)
|
|
160
|
-
parser.add_argument("url", help="URL to capture")
|
|
161
|
-
parser.add_argument("--output", "-o", default="screenshot.png", help="Output file path")
|
|
162
|
-
parser.add_argument(
|
|
163
|
-
"--viewport", "-v",
|
|
164
|
-
choices=list(VIEWPORTS.keys()), default="desktop",
|
|
165
|
-
help="Viewport preset"
|
|
166
|
-
)
|
|
167
|
-
parser.add_argument("--full", action="store_true", help="Capture full page (not just viewport)")
|
|
168
|
-
parser.add_argument("--wait", "-w", type=int, default=2000, help="Wait after load (ms)")
|
|
169
|
-
parser.add_argument("--json", "-j", action="store_true", help="Output metrics as JSON")
|
|
170
|
-
|
|
171
|
-
args = parser.parse_args()
|
|
172
|
-
|
|
173
|
-
import json
|
|
174
|
-
|
|
175
|
-
metrics = capture_screenshot(
|
|
176
|
-
url=args.url,
|
|
177
|
-
output=args.output,
|
|
178
|
-
viewport=args.viewport,
|
|
179
|
-
full_page=args.full,
|
|
180
|
-
wait_ms=args.wait,
|
|
181
|
-
)
|
|
182
|
-
|
|
183
|
-
print(f"Screenshot saved: {args.output}", file=sys.stderr)
|
|
184
|
-
|
|
185
|
-
if args.json:
|
|
186
|
-
print(json.dumps(metrics, indent=2))
|
|
187
|
-
else:
|
|
188
|
-
print(f"\nAbove-the-Fold Analysis ({args.viewport}):")
|
|
189
|
-
print(f" Viewport: {metrics['viewportWidth']}×{metrics['viewportHeight']}")
|
|
190
|
-
print(f" CTAs above fold: {metrics['ctas_above_fold']}")
|
|
191
|
-
for cta in metrics.get("cta_details", []):
|
|
192
|
-
print(f" - \"{cta['text']}\" ({cta['tag']}, top: {cta['top']}px)")
|
|
193
|
-
if metrics.get("largest_image_above_fold"):
|
|
194
|
-
img = metrics["largest_image_above_fold"]
|
|
195
|
-
print(f" Largest image: {img['width']}×{img['height']} at y={img['top']}px")
|
|
196
|
-
print(f" Horizontal scroll: {'⚠️ YES' if metrics['has_horizontal_scroll'] else '✅ No'}")
|
|
197
|
-
print(f" Body font size: {metrics['body_font_size_px']}px {'✅' if metrics['body_font_size_px'] >= 16 else '⚠️ <16px'}")
|
|
198
|
-
print(f" DOM elements: {metrics['dom_element_count']:,}")
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
if __name__ == "__main__":
|
|
202
|
-
main()
|
|
@@ -1,241 +0,0 @@
|
|
|
1
|
-
# SEO Audit Workflow — BMAD+ SEO Engine v2.0
|
|
2
|
-
|
|
3
|
-
> Author: Laurent Rochetta | By Oveanet × Laurent Rochetta
|
|
4
|
-
|
|
5
|
-
## Overview
|
|
6
|
-
|
|
7
|
-
This workflow orchestrates the 3 SEO Engine agents (Scout, Judge, Chief) through 6 phases to produce a comprehensive audit with scored results and actionable fixes.
|
|
8
|
-
|
|
9
|
-
---
|
|
10
|
-
|
|
11
|
-
## Phase 1 — Reconnaissance (Scout solo)
|
|
12
|
-
|
|
13
|
-
**Duration**: ~2 min | **Agent**: Scout (Crawler role)
|
|
14
|
-
|
|
15
|
-
1. Fetch homepage + `/robots.txt` + `/sitemap.xml`
|
|
16
|
-
2. Detect business type (SaaS, e-commerce, local, publisher, agency)
|
|
17
|
-
3. Mini-crawl: discover 10–25 pages via sitemap + internal links
|
|
18
|
-
4. Detect rendering architecture (SSR / CSR / hybrid)
|
|
19
|
-
5. Check for `/llms.txt` and `/llms-full.txt`
|
|
20
|
-
|
|
21
|
-
**Checkpoint**: "Identified a [type] site with [N] pages. Continue full audit?"
|
|
22
|
-
|
|
23
|
-
**Tools**:
|
|
24
|
-
```bash
|
|
25
|
-
python scripts/seo_fetch.py [URL] --json
|
|
26
|
-
python scripts/seo_crawl.py [URL] --depth 2 --max 25 --json
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
---
|
|
30
|
-
|
|
31
|
-
## Phase 2 — Deep Scan (Scout + Judge in PARALLEL)
|
|
32
|
-
|
|
33
|
-
**Duration**: ~5 min | **Agents**: Scout (Inspector) + Judge (Content Expert + Schema Master)
|
|
34
|
-
|
|
35
|
-
### Scout inspects (9 categories):
|
|
36
|
-
1. Crawlability (robots.txt, noindex, crawl depth)
|
|
37
|
-
2. Indexability (canonicals, duplicates, pagination)
|
|
38
|
-
3. Security (HTTPS, HSTS, CSP, X-Frame-Options)
|
|
39
|
-
4. URL Structure (clean URLs, redirects, trailing slashes)
|
|
40
|
-
5. Mobile (viewport, touch targets, font size)
|
|
41
|
-
6. Core Web Vitals signals from source HTML
|
|
42
|
-
7. Structured Data detection (pass to Judge)
|
|
43
|
-
8. JavaScript rendering (CSR/SSR, SPA detection)
|
|
44
|
-
9. IndexNow protocol
|
|
45
|
-
|
|
46
|
-
### Judge analyzes (in parallel):
|
|
47
|
-
1. E-E-A-T evaluation (Experience, Expertise, Authority, Trust)
|
|
48
|
-
2. Content quality (word count, readability, keyword optimization)
|
|
49
|
-
3. Schema validation (JSON-LD, types, deprecation status)
|
|
50
|
-
4. Image audit (alt text, dimensions, format, lazy loading)
|
|
51
|
-
5. Internal/external link analysis
|
|
52
|
-
6. AI content detection markers
|
|
53
|
-
|
|
54
|
-
**Tools**:
|
|
55
|
-
```bash
|
|
56
|
-
python scripts/seo_parse.py page.html --url [URL] --json
|
|
57
|
-
python scripts/seo_screenshot.py [URL] --viewport mobile --json
|
|
58
|
-
python scripts/seo_screenshot.py [URL] --viewport desktop --json
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
---
|
|
62
|
-
|
|
63
|
-
## Phase 3 — AI Readiness & GEO (Judge solo)
|
|
64
|
-
|
|
65
|
-
**Duration**: ~3 min | **Agent**: Judge (GEO Analyst role)
|
|
66
|
-
|
|
67
|
-
1. Check AI crawler access (GPTBot, ClaudeBot, PerplexityBot, OAI-SearchBot)
|
|
68
|
-
2. Verify llms.txt compliance
|
|
69
|
-
3. Check RSL 1.0 licensing
|
|
70
|
-
4. Score passage-level citability (134–167 word blocks)
|
|
71
|
-
5. Analyze brand mention signals
|
|
72
|
-
6. Compute **AI Readiness Score (0–100)**
|
|
73
|
-
|
|
74
|
-
Reference: `ref/geo-signals.md`
|
|
75
|
-
|
|
76
|
-
---
|
|
77
|
-
|
|
78
|
-
## Phase 4 — Scoring & Synthesis (Chief solo)
|
|
79
|
-
|
|
80
|
-
**Duration**: ~2 min | **Agent**: Chief (Scorer role)
|
|
81
|
-
|
|
82
|
-
Compute **SEO Health Score (0–100)** from weighted categories:
|
|
83
|
-
|
|
84
|
-
| Category | Weight |
|
|
85
|
-
|----------|--------|
|
|
86
|
-
| Technical SEO | 20% |
|
|
87
|
-
| Content & E-E-A-T | 22% |
|
|
88
|
-
| On-Page SEO | 18% |
|
|
89
|
-
| Schema | 10% |
|
|
90
|
-
| Performance (CWV) | 12% |
|
|
91
|
-
| AI Readiness (GEO) | 12% |
|
|
92
|
-
| Images | 6% |
|
|
93
|
-
|
|
94
|
-
Output: Score breakdown with visual indicators per category.
|
|
95
|
-
|
|
96
|
-
---
|
|
97
|
-
|
|
98
|
-
## Phase 5 — Action Plan & Auto-Fixes (Chief solo)
|
|
99
|
-
|
|
100
|
-
**Duration**: ~3 min | **Agent**: Chief (Strategist role)
|
|
101
|
-
|
|
102
|
-
1. Classify all issues: 🔴 Critical → 🟠 High → 🟡 Medium → 🟢 Low
|
|
103
|
-
2. Identify Quick Wins (high impact / low effort)
|
|
104
|
-
3. Generate 30/60/90-day roadmap
|
|
105
|
-
4. **Auto-generate code fixes** for:
|
|
106
|
-
- Missing/broken meta tags (title, description)
|
|
107
|
-
- Schema JSON-LD blocks (from templates)
|
|
108
|
-
- robots.txt improvements (AI crawler access)
|
|
109
|
-
- llms.txt file generation
|
|
110
|
-
- Image alt text suggestions
|
|
111
|
-
5. **Checkpoint**: "Here's the plan. Want me to apply the fixes automatically?"
|
|
112
|
-
|
|
113
|
-
---
|
|
114
|
-
|
|
115
|
-
## Phase 5b — PageSpeed Perfection Loop (Scout + Chief iterative)
|
|
116
|
-
|
|
117
|
-
> **This is the battle-tested loop from our oveanet.ch optimization.**
|
|
118
|
-
> Goal: Achieve **100% on all 4 PageSpeed Insights categories** (Performance, Accessibility, Best Practices, SEO).
|
|
119
|
-
|
|
120
|
-
### The Loop
|
|
121
|
-
|
|
122
|
-
```
|
|
123
|
-
┌─────────────────────────────────────┐
|
|
124
|
-
│ 1. Run PageSpeed Insights audit │
|
|
125
|
-
│ (via API or manual) │
|
|
126
|
-
│ │ │
|
|
127
|
-
│ ▼ │
|
|
128
|
-
│ 2. Parse failing audits │
|
|
129
|
-
│ Group by category + priority │
|
|
130
|
-
│ │ │
|
|
131
|
-
│ ▼ │
|
|
132
|
-
│ 3. Apply top-priority fix │
|
|
133
|
-
│ (one fix at a time) │
|
|
134
|
-
│ │ │
|
|
135
|
-
│ ▼ │
|
|
136
|
-
│ 4. Re-run PageSpeed │
|
|
137
|
-
│ Verify fix + no regressions │
|
|
138
|
-
│ │ │
|
|
139
|
-
│ ▼ │
|
|
140
|
-
│ 5. Score improved? │
|
|
141
|
-
│ YES → Continue to next fix │
|
|
142
|
-
│ NO → Revert and try different │
|
|
143
|
-
│ approach │
|
|
144
|
-
│ │ │
|
|
145
|
-
│ ▼ │
|
|
146
|
-
│ 6. All 4 categories = 100%? │
|
|
147
|
-
│ YES → Done ✅ │
|
|
148
|
-
│ NO → Go to step 2 │
|
|
149
|
-
└─────────────────────────────────────┘
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
### PageSpeed Fix Priority Order
|
|
153
|
-
1. **Performance** (hardest — tackle first):
|
|
154
|
-
- Eliminate render-blocking resources
|
|
155
|
-
- Properly size images (WebP/AVIF + responsive)
|
|
156
|
-
- Reduce unused CSS/JS
|
|
157
|
-
- Defer offscreen images
|
|
158
|
-
- Minimize main-thread work
|
|
159
|
-
- Reduce server response time (TTFB)
|
|
160
|
-
- Preload LCP image
|
|
161
|
-
|
|
162
|
-
2. **Accessibility** (usually quick wins):
|
|
163
|
-
- Add alt text to all images
|
|
164
|
-
- Fix color contrast ratios (4.5:1 minimum)
|
|
165
|
-
- Add ARIA labels to interactive elements
|
|
166
|
-
- Ensure heading hierarchy
|
|
167
|
-
- Add lang attribute to HTML
|
|
168
|
-
- Ensure form labels
|
|
169
|
-
|
|
170
|
-
3. **Best Practices**:
|
|
171
|
-
- HTTPS + no mixed content
|
|
172
|
-
- No browser errors in console
|
|
173
|
-
- Remove deprecated APIs
|
|
174
|
-
- Add proper CSP headers
|
|
175
|
-
|
|
176
|
-
4. **SEO** (usually easiest):
|
|
177
|
-
- Add meta description
|
|
178
|
-
- Ensure crawlable links
|
|
179
|
-
- Valid robots.txt
|
|
180
|
-
- Proper viewport meta
|
|
181
|
-
- Descriptive link text
|
|
182
|
-
|
|
183
|
-
### Key Rules
|
|
184
|
-
- **One fix at a time** — never batch multiple changes, you need to isolate impact
|
|
185
|
-
- **Always re-test** — PageSpeed scores can regress with seemingly unrelated changes
|
|
186
|
-
- **Mobile first** — always test mobile viewport (Google uses mobile for indexing)
|
|
187
|
-
- **Field vs Lab** — Lab scores (Lighthouse) can differ from field data (CrUX). Target lab 100% first
|
|
188
|
-
|
|
189
|
-
---
|
|
190
|
-
|
|
191
|
-
## Phase 6 — Monitoring (Scout, optional)
|
|
192
|
-
|
|
193
|
-
**Duration**: ongoing | **Agent**: Scout (Crawler role)
|
|
194
|
-
|
|
195
|
-
1. Save audit results to `.bmad-seo/history/[domain]-[date].json`
|
|
196
|
-
2. On re-audit: compare with previous results
|
|
197
|
-
3. Track: issues resolved, new issues, score evolution
|
|
198
|
-
4. Generate progress report with deltas
|
|
199
|
-
|
|
200
|
-
---
|
|
201
|
-
|
|
202
|
-
## Command Quick Reference
|
|
203
|
-
|
|
204
|
-
```bash
|
|
205
|
-
# Full audit (all 6 phases)
|
|
206
|
-
/seo full https://example.com
|
|
207
|
-
|
|
208
|
-
# Quick audit (Phases 1-4 only)
|
|
209
|
-
/seo quick https://example.com
|
|
210
|
-
|
|
211
|
-
# Individual commands
|
|
212
|
-
/seo technical https://example.com
|
|
213
|
-
/seo content https://example.com
|
|
214
|
-
/seo geo https://example.com
|
|
215
|
-
/seo schema https://example.com
|
|
216
|
-
/seo images https://example.com
|
|
217
|
-
/seo hreflang https://example.com
|
|
218
|
-
|
|
219
|
-
# PageSpeed perfection loop
|
|
220
|
-
/seo pagespeed https://example.com
|
|
221
|
-
|
|
222
|
-
# Strategic planning
|
|
223
|
-
/seo plan saas|ecommerce|local|publisher|agency
|
|
224
|
-
|
|
225
|
-
# Auto-fix generation
|
|
226
|
-
/seo fix
|
|
227
|
-
|
|
228
|
-
# Monitoring
|
|
229
|
-
/seo history
|
|
230
|
-
/seo compare
|
|
231
|
-
```
|
|
232
|
-
|
|
233
|
-
---
|
|
234
|
-
|
|
235
|
-
## Tips for Best Results
|
|
236
|
-
|
|
237
|
-
1. **Start with `/seo full`** for the first audit — it gives you the complete picture
|
|
238
|
-
2. **Use `/seo pagespeed`** after fixing major issues to chase 100% scores
|
|
239
|
-
3. **Re-run monthly** with `/seo compare` to track progress
|
|
240
|
-
4. **Feed the AI crawlers**: Allow GPTBot + ClaudeBot + PerplexityBot in robots.txt
|
|
241
|
-
5. **Check GEO separately**: AI search visibility evolves fast, audit quarterly with `/seo geo`
|
package/oveanet-pack/seo-audit-360/tests/__pycache__/test_crawl.cpython-314-pytest-9.0.2.pyc
DELETED
|
Binary file
|
package/oveanet-pack/seo-audit-360/tests/__pycache__/test_parse.cpython-314-pytest-9.0.2.pyc
DELETED
|
Binary file
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8">
|
|
5
|
-
<title>SEO Test Page — BMAD+ Fixture</title>
|
|
6
|
-
<meta name="description" content="A test page for validating the SEO parse module with known elements.">
|
|
7
|
-
<meta name="robots" content="index, follow">
|
|
8
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
9
|
-
<meta property="og:title" content="SEO Test Page">
|
|
10
|
-
<meta property="og:type" content="website">
|
|
11
|
-
<meta property="og:url" content="https://example.com/test">
|
|
12
|
-
<meta name="twitter:card" content="summary_large_image">
|
|
13
|
-
<meta name="twitter:title" content="SEO Test Page">
|
|
14
|
-
<link rel="canonical" href="https://example.com/test">
|
|
15
|
-
<link rel="alternate" hreflang="en" href="https://example.com/en/test">
|
|
16
|
-
<link rel="alternate" hreflang="fr" href="https://example.com/fr/test">
|
|
17
|
-
<link rel="alternate" hreflang="x-default" href="https://example.com/test">
|
|
18
|
-
</head>
|
|
19
|
-
<body>
|
|
20
|
-
<h1>Main Heading of the Page</h1>
|
|
21
|
-
<p>This is a test paragraph with enough words to verify word count functionality in the parser module. We need at least a few sentences to make the test meaningful and realistic.</p>
|
|
22
|
-
|
|
23
|
-
<h2>Second Level Heading One</h2>
|
|
24
|
-
<p>Content under the first H2. This paragraph adds more text to increase the word count.</p>
|
|
25
|
-
|
|
26
|
-
<h2>Second Level Heading Two</h2>
|
|
27
|
-
<p>Another section with different content about SEO analysis and testing.</p>
|
|
28
|
-
|
|
29
|
-
<h3>Third Level Heading</h3>
|
|
30
|
-
<p>Detailed information under the H3 heading for testing hierarchy detection.</p>
|
|
31
|
-
|
|
32
|
-
<img src="/images/hero.jpg" alt="Hero image for testing" width="800" height="400" loading="lazy">
|
|
33
|
-
<img src="/images/no-alt.jpg" width="200" height="200">
|
|
34
|
-
<img src="/images/empty-alt.jpg" alt="" width="100" height="100">
|
|
35
|
-
|
|
36
|
-
<a href="https://example.com/about">About Us</a>
|
|
37
|
-
<a href="https://example.com/services">Our Services</a>
|
|
38
|
-
<a href="https://external.com/partner" rel="nofollow" target="_blank">Partner Link</a>
|
|
39
|
-
<a href="/relative-link">Relative Link</a>
|
|
40
|
-
|
|
41
|
-
<script type="application/ld+json">
|
|
42
|
-
{
|
|
43
|
-
"@context": "https://schema.org",
|
|
44
|
-
"@type": "Organization",
|
|
45
|
-
"name": "Test Company",
|
|
46
|
-
"url": "https://example.com",
|
|
47
|
-
"logo": "https://example.com/logo.png"
|
|
48
|
-
}
|
|
49
|
-
</script>
|
|
50
|
-
|
|
51
|
-
<script type="application/ld+json">
|
|
52
|
-
{
|
|
53
|
-
"@context": "https://schema.org",
|
|
54
|
-
"@type": "BreadcrumbList",
|
|
55
|
-
"itemListElement": [
|
|
56
|
-
{"@type": "ListItem", "position": 1, "name": "Home", "item": "https://example.com"},
|
|
57
|
-
{"@type": "ListItem", "position": 2, "name": "Test", "item": "https://example.com/test"}
|
|
58
|
-
]
|
|
59
|
-
}
|
|
60
|
-
</script>
|
|
61
|
-
</body>
|
|
62
|
-
</html>
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Tests for seo_apis.py — API response parsing and error handling.
|
|
3
|
-
|
|
4
|
-
Author: Laurent Rochetta
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
import sys
|
|
8
|
-
import os
|
|
9
|
-
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "scripts"))
|
|
10
|
-
|
|
11
|
-
# Temporarily unset API key for error tests
|
|
12
|
-
original_key = os.environ.get("GOOGLE_API_KEY", "")
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
class TestAPIKeyMissing:
|
|
16
|
-
"""Test behavior when GOOGLE_API_KEY is not set."""
|
|
17
|
-
|
|
18
|
-
def setup_method(self):
|
|
19
|
-
os.environ.pop("GOOGLE_API_KEY", None)
|
|
20
|
-
# Reimport to pick up empty key
|
|
21
|
-
import importlib
|
|
22
|
-
import seo_apis
|
|
23
|
-
importlib.reload(seo_apis)
|
|
24
|
-
self.seo_apis = seo_apis
|
|
25
|
-
|
|
26
|
-
def teardown_method(self):
|
|
27
|
-
if original_key:
|
|
28
|
-
os.environ["GOOGLE_API_KEY"] = original_key
|
|
29
|
-
|
|
30
|
-
def test_pagespeed_without_key(self):
|
|
31
|
-
# Force the module to use an empty key
|
|
32
|
-
self.seo_apis.API_KEY = ""
|
|
33
|
-
result = self.seo_apis.run_pagespeed("https://example.com")
|
|
34
|
-
assert result.get("error") is not None
|
|
35
|
-
assert "GOOGLE_API_KEY" in result["error"]
|
|
36
|
-
|
|
37
|
-
def test_crux_without_key(self):
|
|
38
|
-
self.seo_apis.API_KEY = ""
|
|
39
|
-
result = self.seo_apis.run_crux("https://example.com")
|
|
40
|
-
assert result.get("error") is not None
|
|
41
|
-
|
|
42
|
-
def test_rich_results_without_key(self):
|
|
43
|
-
self.seo_apis.API_KEY = ""
|
|
44
|
-
result = self.seo_apis.run_rich_results_test("https://example.com")
|
|
45
|
-
assert result.get("error") is not None
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
class TestResultStructure:
|
|
49
|
-
"""Test that API functions return expected structures."""
|
|
50
|
-
|
|
51
|
-
def setup_method(self):
|
|
52
|
-
import importlib
|
|
53
|
-
import seo_apis
|
|
54
|
-
importlib.reload(seo_apis)
|
|
55
|
-
self.seo_apis = seo_apis
|
|
56
|
-
|
|
57
|
-
def test_pagespeed_result_keys(self):
|
|
58
|
-
self.seo_apis.API_KEY = ""
|
|
59
|
-
result = self.seo_apis.run_pagespeed("https://example.com")
|
|
60
|
-
# Even on error, should have expected structure
|
|
61
|
-
assert "error" in result
|
|
62
|
-
|
|
63
|
-
def test_crux_result_keys(self):
|
|
64
|
-
self.seo_apis.API_KEY = ""
|
|
65
|
-
result = self.seo_apis.run_crux("https://example.com")
|
|
66
|
-
assert "error" in result
|
|
67
|
-
|
|
68
|
-
def test_run_all_structure(self):
|
|
69
|
-
self.seo_apis.API_KEY = ""
|
|
70
|
-
result = self.seo_apis.run_all("https://example.com")
|
|
71
|
-
assert "pagespeed" in result
|
|
72
|
-
assert "crux" in result
|
|
73
|
-
assert "mobile_friendly" in result
|
|
74
|
-
assert "url" in result
|
|
75
|
-
assert "timestamp" in result
|