seo-gravity-mcp 1.0.2 → 1.2.0
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/README.md +118 -105
- package/dist/adapters/adapterRegistry.d.ts +7 -0
- package/dist/adapters/adapterRegistry.js +30 -0
- package/dist/adapters/astroAdapter.d.ts +17 -0
- package/dist/adapters/astroAdapter.js +191 -0
- package/dist/adapters/nextAppAdapter.d.ts +17 -0
- package/dist/adapters/nextAppAdapter.js +265 -0
- package/dist/adapters/nextPagesAdapter.d.ts +17 -0
- package/dist/adapters/nextPagesAdapter.js +209 -0
- package/dist/adapters/remixAdapter.d.ts +16 -0
- package/dist/adapters/remixAdapter.js +118 -0
- package/dist/adapters/staticAdapter.d.ts +16 -0
- package/dist/adapters/staticAdapter.js +117 -0
- package/dist/adapters/svelteKitAdapter.d.ts +16 -0
- package/dist/adapters/svelteKitAdapter.js +122 -0
- package/dist/adapters/types.d.ts +39 -0
- package/dist/adapters/types.js +1 -0
- package/dist/adapters/viteReactAdapter.d.ts +16 -0
- package/dist/adapters/viteReactAdapter.js +189 -0
- package/dist/benchmark/correlationBenchmark.d.ts +1 -0
- package/dist/benchmark/correlationBenchmark.js +96 -0
- package/dist/benchmark/invariantBenchmark.d.ts +1 -0
- package/dist/benchmark/invariantBenchmark.js +47 -0
- package/dist/cli.d.ts +8 -0
- package/dist/cli.js +162 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +8 -646
- package/dist/invariants/registry.d.ts +12 -0
- package/dist/invariants/registry.js +178 -0
- package/dist/invariants/types.d.ts +38 -0
- package/dist/invariants/types.js +1 -0
- package/dist/providers/providerRegistry.d.ts +65 -0
- package/dist/providers/providerRegistry.js +64 -0
- package/dist/providers/types.d.ts +22 -0
- package/dist/providers/types.js +1 -0
- package/dist/server/registry.d.ts +3 -0
- package/dist/server/registry.js +559 -0
- package/dist/server/server.d.ts +70 -0
- package/dist/server/server.js +53 -0
- package/dist/server/types.d.ts +7 -0
- package/dist/server/types.js +1 -0
- package/dist/test.js +108 -138
- package/dist/tools/orchestration.js +23 -21
- package/dist/types/canonical.d.ts +64 -0
- package/dist/types/canonical.js +1 -0
- package/dist/types/findings.d.ts +22 -0
- package/dist/types/findings.js +1 -1
- package/dist/types/seo.d.ts +24 -0
- package/dist/utils/astLocator.d.ts +14 -0
- package/dist/utils/astLocator.js +137 -0
- package/dist/utils/cacheManager.d.ts +29 -0
- package/dist/utils/cacheManager.js +60 -0
- package/dist/utils/experimentEngine.d.ts +26 -0
- package/dist/utils/experimentEngine.js +41 -0
- package/dist/utils/findingEngine.d.ts +2 -1
- package/dist/utils/findingEngine.js +1 -0
- package/dist/utils/gitDiffEngine.d.ts +28 -0
- package/dist/utils/gitDiffEngine.js +109 -0
- package/dist/utils/opportunityEngine.d.ts +19 -0
- package/dist/utils/opportunityEngine.js +61 -0
- package/dist/utils/projectScanner.d.ts +4 -2
- package/dist/utils/projectScanner.js +14 -346
- package/dist/utils/sarifExporter.d.ts +64 -0
- package/dist/utils/sarifExporter.js +81 -0
- package/dist/utils/snapshotEngine.d.ts +3 -1
- package/dist/utils/snapshotEngine.js +232 -94
- package/package.json +8 -5
package/README.md
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
# 🚀 SEO Gravity
|
|
1
|
+
# 🚀 SEO Gravity (v1.2.0)
|
|
2
2
|
|
|
3
3
|
<div align="center">
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/seo-gravity-mcp)
|
|
6
6
|
[](https://www.npmjs.com/package/seo-gravity-mcp)
|
|
7
7
|

|
|
8
|
+

|
|
9
|
+

|
|
8
10
|

|
|
9
|
-

|
|
10
|
-

|
|
11
11
|

|
|
12
12
|
|
|
13
|
-
**The
|
|
13
|
+
**The SEO Engineering Infrastructure Layer for AI Coding Agents & CI/CD Pipelines.**
|
|
14
14
|
|
|
15
|
-
[⚡ Instant Start](#-instant-start) • [
|
|
15
|
+
[⚡ Instant Start](#-instant-start) • [💻 CLI & CI/CD](#-command-line--cicd-runner) • [🛡️ Invariant Truth Layer](#-formal-seo-invariant-registry) • [🏛 5-Layer Architecture](#-5-layer-system-architecture) • [🧩 Framework Adapters](#-modular-framework-adapters) • [🛠 Tool Catalog (35 Tools)](#-complete-tool-catalog) • [📄 License](#-license)
|
|
16
16
|
|
|
17
17
|
</div>
|
|
18
18
|
|
|
@@ -20,67 +20,132 @@
|
|
|
20
20
|
|
|
21
21
|
## 🌟 What is SEO Gravity?
|
|
22
22
|
|
|
23
|
-
**SEO Gravity**
|
|
23
|
+
**SEO Gravity** is an **SEO engineering infrastructure layer** designed specifically for AI coding agents (Antigravity, Cursor, Claude Code, Windsurf, Cline, Roo Code) and automated CI/CD pipelines.
|
|
24
24
|
|
|
25
|
-
Instead of
|
|
25
|
+
Instead of operating as a passive reporting tool, SEO Gravity connects **rendered web observations** directly to **source code AST nodes and line numbers** across modern web frameworks (Next.js App/Pages Router, Astro, Vite/React, Remix, SvelteKit), generates actionable code fix blueprints, and enforces invariant-based regression checks.
|
|
26
26
|
|
|
27
27
|
```text
|
|
28
28
|
┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
|
|
29
29
|
│ 1. DISCOVER │ ──▶ │ 2. OBSERVE │ ──▶ │ 3. DIAGNOSE │ ──▶ │ 4. FIX │ ──▶ │ 5. VERIFY │
|
|
30
|
-
│ Framework │ │ Live/Dev SSR │ │
|
|
31
|
-
│
|
|
32
|
-
│ Source Map │ │
|
|
30
|
+
│ Framework │ │ Live/Dev SSR │ │ AST Line Nos │ │ AI Modifies │ │ Invariant │
|
|
31
|
+
│ Adapters │ │ Crawl Graph │ │ Prioritized │ │ Components │ │ Regression │
|
|
32
|
+
│ Source Map │ │ Provenance │ │ Action Plan │ │ Snippets │ │ Git Stamping │
|
|
33
33
|
└──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘
|
|
34
34
|
```
|
|
35
35
|
|
|
36
36
|
---
|
|
37
37
|
|
|
38
|
-
##
|
|
38
|
+
## 🏛 5-Layer System Architecture
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
40
|
+
```text
|
|
41
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
42
|
+
│ 1. AGENT & CLI INTERFACE │
|
|
43
|
+
│ MCP Server (35 tools) + Standalone CLI (`seo-gravity`) │
|
|
44
|
+
├─────────────────────────────────────────────────────────────┤
|
|
45
|
+
│ 2. INTELLIGENCE & REASONING LAYER │
|
|
46
|
+
│ Root-cause diagnosis, deterministic priority, fix plans │
|
|
47
|
+
├─────────────────────────────────────────────────────────────┤
|
|
48
|
+
│ 3. ANALYSIS & INVARIANT LAYER │
|
|
49
|
+
│ SEO Invariants (HTTP 200, canonical, schema, noindex) │
|
|
50
|
+
├─────────────────────────────────────────────────────────────┤
|
|
51
|
+
│ 4. OBSERVATION LAYER (Factual Raw State) │
|
|
52
|
+
│ Observations ≠ Findings; AST inspection, headers, DOM │
|
|
53
|
+
├─────────────────────────────────────────────────────────────┤
|
|
54
|
+
│ 5. PROVIDER & ADAPTER LAYER │
|
|
55
|
+
│ Framework Adapters (Next, Astro, Vite), Providers, Cache │
|
|
56
|
+
└─────────────────────────────────────────────────────────────┘
|
|
57
|
+
```
|
|
46
58
|
|
|
47
59
|
---
|
|
48
60
|
|
|
49
|
-
##
|
|
61
|
+
## 🛡️ Formal SEO Invariant Registry
|
|
62
|
+
|
|
63
|
+
SEO Gravity enforces strict **semantic invariants** rather than heuristic score drops. A regression occurs only when a previously satisfied invariant is violated:
|
|
50
64
|
|
|
51
|
-
|
|
65
|
+
| Invariant ID | Name | Target Scope | Description |
|
|
66
|
+
| :--- | :--- | :--- | :--- |
|
|
67
|
+
| `INV-HTTP-200` | HTTP Success Status | Route | Published routes must return HTTP 200 OK without 4xx/5xx errors. |
|
|
68
|
+
| `INV-CANONICAL-RESOLVES` | Canonical URL Declaration | Page | Indexable pages must declare a canonical URL to prevent duplicate indexing. |
|
|
69
|
+
| `INV-TITLE-PRESENT` | Title Tag Metadata | Page | Pages must declare unique, descriptive title metadata in AST components. |
|
|
70
|
+
| `INV-LINK-ACCESSIBLE` | Link Reachability | Crawl Graph | Public pages must be reachable via internal links (0 orphan pages). |
|
|
71
|
+
| `INV-ROBOTS-ALLOWED` | Robots Configuration | Site | Project must provide `robots.txt` or `robots.ts` specifying bot directives. |
|
|
72
|
+
| `INV-SITEMAP-PRESENT` | XML Sitemap Configuration | Site | Project must publish `sitemap.xml` or dynamic `sitemap.ts`. |
|
|
73
|
+
| `INV-LLMS-TXT` | AI Context Documentation | Site | Project should provide `/llms.txt` for AI agent discovery. |
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## 🧩 Modular Framework Adapters & Benchmark
|
|
78
|
+
|
|
79
|
+
Tested with **100% correlation accuracy** across 7 web application frameworks:
|
|
80
|
+
|
|
81
|
+
| Framework Adapter | Detection | Route Discovery | AST Inspection | Benchmark |
|
|
82
|
+
| :--- | :--- | :--- | :--- | :---: |
|
|
83
|
+
| **`NextAppAdapter`** | `next` + `app/` | App Router hierarchy, route groups `(group)`, dynamic `[slug]` | Pinpoints `metadata` export & `generateMetadata()` lines | **100%** |
|
|
84
|
+
| **`NextPagesAdapter`** | `next` + `pages/` | Pages router hierarchy, dynamic `[id]` | Scans `<Head>` & `<NextSeo>` JSX components | **100%** |
|
|
85
|
+
| **`AstroAdapter`** | `astro` + `src/pages` | `.astro`, `.md`, `.mdx` pages | Scans Astro frontmatter & `<title>` tags | **100%** |
|
|
86
|
+
| **`ViteReactAdapter`** | `vite` / `react` | `index.html` + React Router routes | Scans `<Helmet>` & route definition trees | **100%** |
|
|
87
|
+
| **`RemixAdapter`** | `@remix-run/react` | `app/routes/` | Inspects `meta` and `links` export functions | **100%** |
|
|
88
|
+
| **`SvelteKitAdapter`** | `@sveltejs/kit` | `src/routes/` (`+page.svelte`) | Scans `<svelte:head>` components | **100%** |
|
|
89
|
+
| **`StaticAdapter`** | Static HTML files | Recursive HTML directory traversal | Direct DOM / meta element inspection | **100%** |
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## 💻 Command-Line & CI/CD Runner (SARIF Ready)
|
|
94
|
+
|
|
95
|
+
SEO Gravity includes a standalone CLI binary (`seo-gravity`) with native **SARIF v2.1.0** export for GitHub Code Scanning:
|
|
52
96
|
|
|
53
97
|
```bash
|
|
54
|
-
|
|
98
|
+
# 1. Audit project (Terminal / JSON / SARIF)
|
|
99
|
+
npx seo-gravity-mcp audit --project ./my-app
|
|
100
|
+
npx seo-gravity-mcp audit --project ./my-app --format sarif --output seo-findings.sarif
|
|
101
|
+
|
|
102
|
+
# 2. Create baseline snapshot
|
|
103
|
+
npx seo-gravity-mcp snapshot --project ./my-app --output baseline.json
|
|
104
|
+
|
|
105
|
+
# 3. Check for SEO regressions in CI/CD PR gate (Exits with 0 or 1)
|
|
106
|
+
npx seo-gravity-mcp check --project ./my-app --baseline baseline.json
|
|
107
|
+
|
|
108
|
+
# 4. Targeted Semantic Git differential audit on modified routes only
|
|
109
|
+
npx seo-gravity-mcp diff --project ./my-app --base-ref origin/main
|
|
55
110
|
```
|
|
56
111
|
|
|
57
|
-
|
|
112
|
+
### GitHub Actions CI/CD Integration
|
|
58
113
|
|
|
59
|
-
```
|
|
60
|
-
|
|
114
|
+
```yaml
|
|
115
|
+
name: SEO Quality Gate
|
|
116
|
+
|
|
117
|
+
on: [push, pull_request]
|
|
118
|
+
|
|
119
|
+
jobs:
|
|
120
|
+
seo-check:
|
|
121
|
+
runs-on: ubuntu-latest
|
|
122
|
+
steps:
|
|
123
|
+
- uses: actions/checkout@v4
|
|
124
|
+
- uses: actions/setup-node@v4
|
|
125
|
+
with:
|
|
126
|
+
node-version: 20
|
|
127
|
+
|
|
128
|
+
- name: Run SEO Invariant Regression Check
|
|
129
|
+
run: npx seo-gravity-mcp check --project . --baseline .seo-baseline.json --format sarif --output results.sarif
|
|
130
|
+
|
|
131
|
+
- name: Upload SARIF to GitHub Security Tab
|
|
132
|
+
uses: github/codeql-action/upload-sarif@v3
|
|
133
|
+
if: always()
|
|
134
|
+
with:
|
|
135
|
+
sarif_file: results.sarif
|
|
61
136
|
```
|
|
62
137
|
|
|
63
138
|
---
|
|
64
139
|
|
|
65
|
-
##
|
|
66
|
-
|
|
67
|
-
### 1. 🟣 Claude Desktop
|
|
68
|
-
Add to `claude_desktop_config.json`:
|
|
140
|
+
## ⚡ Instant Start (MCP Server)
|
|
69
141
|
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
"mcpServers": {
|
|
73
|
-
"seo-gravity": {
|
|
74
|
-
"command": "npx",
|
|
75
|
-
"args": ["-y", "seo-gravity-mcp"]
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
}
|
|
142
|
+
```bash
|
|
143
|
+
npx -y seo-gravity-mcp
|
|
79
144
|
```
|
|
80
145
|
|
|
81
|
-
###
|
|
82
|
-
Add to `.cursor/mcp.json`:
|
|
146
|
+
### AI Client Configurations
|
|
83
147
|
|
|
148
|
+
#### Cursor (`.cursor/mcp.json`)
|
|
84
149
|
```json
|
|
85
150
|
{
|
|
86
151
|
"mcpServers": {
|
|
@@ -92,9 +157,7 @@ Add to `.cursor/mcp.json`:
|
|
|
92
157
|
}
|
|
93
158
|
```
|
|
94
159
|
|
|
95
|
-
|
|
96
|
-
Add to `mcp_config.json`:
|
|
97
|
-
|
|
160
|
+
#### Claude Desktop (`claude_desktop_config.json`)
|
|
98
161
|
```json
|
|
99
162
|
{
|
|
100
163
|
"mcpServers": {
|
|
@@ -108,81 +171,31 @@ Add to `mcp_config.json`:
|
|
|
108
171
|
|
|
109
172
|
---
|
|
110
173
|
|
|
111
|
-
## 🛠 Complete Tool Catalog
|
|
174
|
+
## 🛠 Complete Tool Catalog
|
|
112
175
|
|
|
113
176
|
### Layer 0: Agent Orchestration & Remediation (Flagship)
|
|
114
177
|
| Tool Name | Operational Class | Description |
|
|
115
178
|
| :--- | :--- | :--- |
|
|
116
179
|
| `seo_project_audit` | **Planning / Audit** | Scans workspace framework, discovers routes, correlates source files, builds crawl graph, and computes multidimensional SEO health. |
|
|
117
|
-
| `seo_diagnose` | **Planning / Diagnose** | Deep root-cause inspection linking an observed issue/URL back to specific source code
|
|
180
|
+
| `seo_diagnose` | **Planning / Diagnose** | Deep root-cause AST inspection linking an observed issue/URL back to specific source code line ranges with ready-to-use fix blueprints. |
|
|
118
181
|
| `seo_prioritize` | **Planning / Strategy** | Ranks findings into Quick Wins, Critical Blockers, and Architectural Improvements using `(Impact × Confidence × Reach) / Effort`. |
|
|
119
182
|
| `seo_fix_plan` | **Planning / Execution** | Generates an end-to-end code modification and verification plan for AI coding agents. |
|
|
120
|
-
| `seo_snapshot_create` | **Verification** | Creates and saves a project SEO baseline snapshot adhering to
|
|
121
|
-
| `seo_snapshot_compare` | **Verification** |
|
|
183
|
+
| `seo_snapshot_create` | **Verification** | Creates and saves a project SEO baseline snapshot with Git commit stamping adhering to schema `seo.gravity/v1`. |
|
|
184
|
+
| `seo_snapshot_compare` | **Verification** | Invariant-based diffing of two snapshots to isolate resolved findings, new regressions, and multidimensional score deltas. |
|
|
122
185
|
| `seo_regression_check` | **Verification / CI** | Automated pass/fail regression check with alert messages for CI/CD gates and PR bots. |
|
|
123
186
|
|
|
124
|
-
###
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
### Layer 2: Generative Engine Optimization (GEO & AEO)
|
|
134
|
-
| Tool Name | Description |
|
|
135
|
-
| :--- | :--- |
|
|
136
|
-
| `seo_geo_ai_readiness_audit` | Audits content for AI Overviews & Perplexity citation readiness with structured retrieval signals and evidence tiers. |
|
|
137
|
-
| `seo_llms_txt_generate` | Generates standard `/llms.txt` and `/llms-full.txt` markdown context files for AI search bots. |
|
|
138
|
-
| `seo_ai_bots_robots_audit` | Audits robots.txt permissions specifically for AI crawlers (GPTBot, ClaudeBot, PerplexityBot, Google-Extended). |
|
|
139
|
-
|
|
140
|
-
### Layer 3: Information Gain & E-E-A-T
|
|
141
|
-
| Tool Name | Description |
|
|
142
|
-
| :--- | :--- |
|
|
143
|
-
| `seo_information_gain_score` | Quantifies content novelty vs top 10 SERPs to prevent generic AI fluff penalties. |
|
|
144
|
-
| `seo_eeat_audit` | Audits Google E-E-A-T trust signals (Person schema, author bylines, sameAs Wikidata/LinkedIn, publish/modified dates). |
|
|
145
|
-
|
|
146
|
-
### Layer 4: On-Page & Content Strategy
|
|
147
|
-
| Tool Name | Description |
|
|
148
|
-
| :--- | :--- |
|
|
149
|
-
| `seo_onpage_audit` | Deep on-page audit of pixel width, meta CTR, heading hierarchy, image alt, and slug optimization. |
|
|
150
|
-
| `seo_content_brief_generate` | Generates data-backed Content Outline & Brief with target word count, H1/H2/H3 structure, and FAQs. |
|
|
151
|
-
| `seo_readability_score` | Computes Flesch Reading Ease, Flesch-Kincaid Grade Level, and Gunning Fog index. |
|
|
152
|
-
|
|
153
|
-
### Layer 5: Technical SEO & JavaScript Hydration
|
|
154
|
-
| Tool Name | Description |
|
|
155
|
-
| :--- | :--- |
|
|
156
|
-
| `seo_technical_audit` | Inspects HTTP status codes, redirect chains, canonical consistency, meta robots (`noindex` leaks), and SSL. |
|
|
157
|
-
| `seo_js_rendering_diff` | Compares raw server HTML vs hydrated client DOM (JavaScript SEO) to detect client-only content or broken tags. |
|
|
158
|
-
| `seo_robots_txt_validate` | Validates bot crawlability against specific paths and User-Agents. |
|
|
159
|
-
| `seo_sitemap_inspect` | Validates XML sitemaps, sitemap indexes, lastmod timestamps, and URL limit constraints. |
|
|
160
|
-
| `seo_internal_links_analyze` | Analyzes internal link equity distribution, anchor texts, generic anchors, and nofollow flags. |
|
|
161
|
-
|
|
162
|
-
### Layer 6: Keyword Research & Intent
|
|
163
|
-
| Tool Name | Description |
|
|
164
|
-
| :--- | :--- |
|
|
165
|
-
| `seo_keyword_suggestions` | Generates keyword suggestions using Google Autocomplete and Alphabet Soup expansion. |
|
|
166
|
-
| `seo_questions_find` | Finds question queries asked by users across Google (Who, What, Where, When, Why, How, Can). |
|
|
167
|
-
| `seo_keyword_cluster` | Clusters keyword lists into Topic Pillars and Supporting Articles using semantic similarity. |
|
|
168
|
-
| `seo_search_intent_classify` | Classifies keywords into Informational, Navigational, Commercial, or Transactional intent. |
|
|
169
|
-
|
|
170
|
-
### Layer 7: Schema & Entity Graph
|
|
171
|
-
| Tool Name | Description |
|
|
172
|
-
| :--- | :--- |
|
|
173
|
-
| `seo_entity_salience_map` | Extracts named entities, computes salience scores, and generates SPO relationship triples. |
|
|
174
|
-
| `seo_schema_generate` | Generates validated Schema.org JSON-LD scripts (Article, FAQPage, Product, LocalBusiness, Organization). |
|
|
175
|
-
| `seo_schema_validate` | Validates structured data against Schema.org and Google Rich Result criteria. |
|
|
176
|
-
|
|
177
|
-
### Layer 8: Performance & Maintenance
|
|
178
|
-
| Tool Name | Description |
|
|
179
|
-
| :--- | :--- |
|
|
180
|
-
| `seo_pagespeed_audit` | Checks Core Web Vitals (LCP, FCP, CLS, TTFB) with performance optimization fixes. |
|
|
181
|
-
| `seo_indexnow_submit` | Submits newly created or updated URLs directly to Bing & Yandex via the IndexNow API protocol. |
|
|
182
|
-
| `seo_content_decay_audit` | Scans content for freshness decay (stale year references, outdated stats, broken external links). |
|
|
187
|
+
### Layers 1–8: Specialized SEO & GEO Engines
|
|
188
|
+
- **Layer 1 (SERP & Competitors)**: `seo_serp_analyze`, `seo_competitor_content_gap`, `seo_competitor_profile`, `seo_competitor_diff`, `seo_forum_discussions_pulse`
|
|
189
|
+
- **Layer 2 (GEO & AI Search)**: `seo_geo_ai_readiness_audit`, `seo_llms_txt_generate`, `seo_ai_bots_robots_audit`
|
|
190
|
+
- **Layer 3 (Info Gain & E-E-A-T)**: `seo_information_gain_score`, `seo_eeat_audit`
|
|
191
|
+
- **Layer 4 (On-Page)**: `seo_onpage_audit`, `seo_content_brief_generate`, `seo_readability_score`
|
|
192
|
+
- **Layer 5 (Technical SEO)**: `seo_technical_audit`, `seo_js_rendering_diff`, `seo_robots_txt_validate`, `seo_sitemap_inspect`, `seo_internal_links_analyze`
|
|
193
|
+
- **Layer 6 (Keywords & Intent)**: `seo_keyword_suggestions`, `seo_questions_find`, `seo_keyword_cluster`, `seo_search_intent_classify`
|
|
194
|
+
- **Layer 7 (Schema & Entity)**: `seo_entity_salience_map`, `seo_schema_generate`, `seo_schema_validate`
|
|
195
|
+
- **Layer 8 (Speed & Maintenance)**: `seo_pagespeed_audit`, `seo_indexnow_submit`, `seo_content_decay_audit`
|
|
183
196
|
|
|
184
197
|
---
|
|
185
198
|
|
|
186
199
|
## 📄 License
|
|
187
200
|
|
|
188
|
-
MIT License. Free for open-source and commercial use.
|
|
201
|
+
MIT License. Copyright (c) 2026 thedevbob005. Free for open-source and commercial use.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { FrameworkAdapter } from './types.js';
|
|
2
|
+
export declare class AdapterRegistry {
|
|
3
|
+
private adapters;
|
|
4
|
+
getAdapterForProject(projectDir: string): FrameworkAdapter;
|
|
5
|
+
getAdapterById(id: string): FrameworkAdapter | undefined;
|
|
6
|
+
}
|
|
7
|
+
export declare const defaultAdapterRegistry: AdapterRegistry;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { NextAppAdapter } from './nextAppAdapter.js';
|
|
2
|
+
import { NextPagesAdapter } from './nextPagesAdapter.js';
|
|
3
|
+
import { AstroAdapter } from './astroAdapter.js';
|
|
4
|
+
import { ViteReactAdapter } from './viteReactAdapter.js';
|
|
5
|
+
import { RemixAdapter } from './remixAdapter.js';
|
|
6
|
+
import { SvelteKitAdapter } from './svelteKitAdapter.js';
|
|
7
|
+
import { StaticAdapter } from './staticAdapter.js';
|
|
8
|
+
export class AdapterRegistry {
|
|
9
|
+
adapters = [
|
|
10
|
+
new NextAppAdapter(),
|
|
11
|
+
new NextPagesAdapter(),
|
|
12
|
+
new AstroAdapter(),
|
|
13
|
+
new RemixAdapter(),
|
|
14
|
+
new SvelteKitAdapter(),
|
|
15
|
+
new ViteReactAdapter(),
|
|
16
|
+
new StaticAdapter() // fallback
|
|
17
|
+
];
|
|
18
|
+
getAdapterForProject(projectDir) {
|
|
19
|
+
for (const adapter of this.adapters) {
|
|
20
|
+
if (adapter.detect(projectDir)) {
|
|
21
|
+
return adapter;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return this.adapters[this.adapters.length - 1]; // fallback static
|
|
25
|
+
}
|
|
26
|
+
getAdapterById(id) {
|
|
27
|
+
return this.adapters.find(a => a.id === id);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
export const defaultAdapterRegistry = new AdapterRegistry();
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { FrameworkAdapter, MetadataLocationInfo, CanonicalLocationInfo, SchemaLocationInfo } from './types.js';
|
|
2
|
+
import { DiscoveredRoute, ProjectFrameworkInfo, RouteSourceMapping } from '../types/findings.js';
|
|
3
|
+
export declare class AstroAdapter implements FrameworkAdapter {
|
|
4
|
+
id: string;
|
|
5
|
+
name: string;
|
|
6
|
+
detect(projectDir: string): boolean;
|
|
7
|
+
getProjectInfo(projectDir: string): ProjectFrameworkInfo;
|
|
8
|
+
discoverRoutes(projectDir: string): DiscoveredRoute[];
|
|
9
|
+
mapRouteToSource(targetUrl: string, routes: DiscoveredRoute[]): RouteSourceMapping;
|
|
10
|
+
findMetadataImplementation(projectDir: string, route: DiscoveredRoute): Promise<MetadataLocationInfo>;
|
|
11
|
+
findCanonicalDeclaration(projectDir: string, route: DiscoveredRoute): Promise<CanonicalLocationInfo>;
|
|
12
|
+
findSchemaDeclaration(projectDir: string, route: DiscoveredRoute): Promise<SchemaLocationInfo>;
|
|
13
|
+
findRobotsConfig(projectDir: string): string | null;
|
|
14
|
+
findSitemapConfig(projectDir: string): string | null;
|
|
15
|
+
findLlmsTxt(projectDir: string): string | null;
|
|
16
|
+
private extractDynamicParams;
|
|
17
|
+
}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
export class AstroAdapter {
|
|
4
|
+
id = 'astro';
|
|
5
|
+
name = 'Astro Framework';
|
|
6
|
+
detect(projectDir) {
|
|
7
|
+
const hasConfig = fs.existsSync(path.join(projectDir, 'astro.config.mjs')) ||
|
|
8
|
+
fs.existsSync(path.join(projectDir, 'astro.config.ts'));
|
|
9
|
+
if (hasConfig)
|
|
10
|
+
return true;
|
|
11
|
+
const pkgPath = path.join(projectDir, 'package.json');
|
|
12
|
+
if (fs.existsSync(pkgPath)) {
|
|
13
|
+
try {
|
|
14
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
15
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
16
|
+
return Boolean(deps['astro']);
|
|
17
|
+
}
|
|
18
|
+
catch { }
|
|
19
|
+
}
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
getProjectInfo(projectDir) {
|
|
23
|
+
const pkgPath = path.join(projectDir, 'package.json');
|
|
24
|
+
let pkg = {};
|
|
25
|
+
if (fs.existsSync(pkgPath)) {
|
|
26
|
+
try {
|
|
27
|
+
pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
28
|
+
}
|
|
29
|
+
catch { }
|
|
30
|
+
}
|
|
31
|
+
const hasSrcPages = fs.existsSync(path.join(projectDir, 'src/pages'));
|
|
32
|
+
const routesDir = hasSrcPages ? 'src/pages' : 'pages';
|
|
33
|
+
return {
|
|
34
|
+
framework: 'astro',
|
|
35
|
+
name: pkg.name || path.basename(projectDir),
|
|
36
|
+
version: pkg.dependencies?.astro || pkg.devDependencies?.astro,
|
|
37
|
+
packageManager: fs.existsSync(path.join(projectDir, 'pnpm-lock.yaml'))
|
|
38
|
+
? 'pnpm'
|
|
39
|
+
: fs.existsSync(path.join(projectDir, 'yarn.lock'))
|
|
40
|
+
? 'yarn'
|
|
41
|
+
: 'npm',
|
|
42
|
+
hasTypeScript: fs.existsSync(path.join(projectDir, 'tsconfig.json')),
|
|
43
|
+
hasSitemapConfig: Boolean(this.findSitemapConfig(projectDir)),
|
|
44
|
+
hasRobotsConfig: Boolean(this.findRobotsConfig(projectDir)),
|
|
45
|
+
hasLlmsTxt: Boolean(this.findLlmsTxt(projectDir)),
|
|
46
|
+
rootDir: path.resolve(projectDir),
|
|
47
|
+
routesDir,
|
|
48
|
+
devCommand: 'npm run dev',
|
|
49
|
+
defaultDevPort: 4321
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
discoverRoutes(projectDir) {
|
|
53
|
+
const info = this.getProjectInfo(projectDir);
|
|
54
|
+
const fullRoutesDir = path.join(projectDir, info.routesDir || 'src/pages');
|
|
55
|
+
const discovered = [];
|
|
56
|
+
if (!fs.existsSync(fullRoutesDir))
|
|
57
|
+
return discovered;
|
|
58
|
+
const scanDir = (currentDir, routePrefix) => {
|
|
59
|
+
const entries = fs.readdirSync(currentDir, { withFileTypes: true });
|
|
60
|
+
for (const entry of entries) {
|
|
61
|
+
const entryPath = path.join(currentDir, entry.name);
|
|
62
|
+
if (entry.isDirectory()) {
|
|
63
|
+
const nextPrefix = routePrefix === '/' ? `/${entry.name}` : `${routePrefix}/${entry.name}`;
|
|
64
|
+
scanDir(entryPath, nextPrefix);
|
|
65
|
+
}
|
|
66
|
+
else if (entry.isFile() && /\.(astro|md|mdx)$/.test(entry.name)) {
|
|
67
|
+
const baseName = entry.name.replace(/\.(astro|md|mdx)$/, '');
|
|
68
|
+
let routePath = routePrefix;
|
|
69
|
+
if (baseName !== 'index') {
|
|
70
|
+
routePath = routePrefix === '/' ? `/${baseName}` : `${routePrefix}/${baseName}`;
|
|
71
|
+
}
|
|
72
|
+
if (routePath === '')
|
|
73
|
+
routePath = '/';
|
|
74
|
+
const relFile = path.relative(projectDir, entryPath).replace(/\\/g, '/');
|
|
75
|
+
const content = fs.readFileSync(entryPath, 'utf-8');
|
|
76
|
+
const dynamicParams = this.extractDynamicParams(routePath);
|
|
77
|
+
discovered.push({
|
|
78
|
+
routePath,
|
|
79
|
+
sourceFilePath: relFile,
|
|
80
|
+
isDynamic: dynamicParams.length > 0,
|
|
81
|
+
dynamicParams,
|
|
82
|
+
routeType: 'page',
|
|
83
|
+
hasMetadataExport: /title\s*=|description\s*=|astro:head|<title/.test(content),
|
|
84
|
+
hasGenerateMetadata: false,
|
|
85
|
+
hasSchemaMarkup: /application\/ld\+json/.test(content),
|
|
86
|
+
hasHeadComponent: /<head|astro:head|<title/.test(content),
|
|
87
|
+
isClientComponent: false
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
scanDir(fullRoutesDir, '/');
|
|
93
|
+
return discovered;
|
|
94
|
+
}
|
|
95
|
+
mapRouteToSource(targetUrl, routes) {
|
|
96
|
+
let urlPath = targetUrl;
|
|
97
|
+
try {
|
|
98
|
+
if (targetUrl.startsWith('http')) {
|
|
99
|
+
urlPath = new URL(targetUrl).pathname;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
urlPath = targetUrl;
|
|
104
|
+
}
|
|
105
|
+
urlPath = urlPath.replace(/\/$/, '') || '/';
|
|
106
|
+
for (const r of routes) {
|
|
107
|
+
const clean = r.routePath.replace(/\/$/, '') || '/';
|
|
108
|
+
if (clean === urlPath) {
|
|
109
|
+
return {
|
|
110
|
+
urlPath,
|
|
111
|
+
matchedRoute: r,
|
|
112
|
+
sourceFilePath: r.sourceFilePath,
|
|
113
|
+
confidence: 1.0,
|
|
114
|
+
resolutionMethod: 'exact_match'
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return {
|
|
119
|
+
urlPath,
|
|
120
|
+
confidence: 0.0,
|
|
121
|
+
resolutionMethod: 'unmapped'
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
async findMetadataImplementation(projectDir, route) {
|
|
125
|
+
const fullPath = path.join(projectDir, route.sourceFilePath);
|
|
126
|
+
if (!fs.existsSync(fullPath))
|
|
127
|
+
return { hasMetadata: false, type: 'none' };
|
|
128
|
+
const content = fs.readFileSync(fullPath, 'utf-8');
|
|
129
|
+
const hasMeta = /<title\b|<meta\s+name=["']description["']/i.test(content) || /title\s*:/i.test(content);
|
|
130
|
+
return {
|
|
131
|
+
hasMetadata: hasMeta,
|
|
132
|
+
type: hasMeta ? 'jsx_head' : 'none'
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
async findCanonicalDeclaration(projectDir, route) {
|
|
136
|
+
const fullPath = path.join(projectDir, route.sourceFilePath);
|
|
137
|
+
if (!fs.existsSync(fullPath))
|
|
138
|
+
return { hasCanonical: false, type: 'none' };
|
|
139
|
+
const content = fs.readFileSync(fullPath, 'utf-8');
|
|
140
|
+
const hasCanonical = /rel=["']canonical["']/i.test(content);
|
|
141
|
+
return {
|
|
142
|
+
hasCanonical,
|
|
143
|
+
type: hasCanonical ? 'link_jsx_tag' : 'none'
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
async findSchemaDeclaration(projectDir, route) {
|
|
147
|
+
const fullPath = path.join(projectDir, route.sourceFilePath);
|
|
148
|
+
if (!fs.existsSync(fullPath))
|
|
149
|
+
return { hasSchema: false, typesFound: [] };
|
|
150
|
+
const content = fs.readFileSync(fullPath, 'utf-8');
|
|
151
|
+
const hasSchema = /application\/ld\+json/i.test(content);
|
|
152
|
+
return {
|
|
153
|
+
hasSchema,
|
|
154
|
+
typesFound: hasSchema ? ['JSON-LD'] : []
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
findRobotsConfig(projectDir) {
|
|
158
|
+
const paths = ['public/robots.txt', 'robots.txt'];
|
|
159
|
+
for (const p of paths) {
|
|
160
|
+
if (fs.existsSync(path.join(projectDir, p)))
|
|
161
|
+
return p;
|
|
162
|
+
}
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
findSitemapConfig(projectDir) {
|
|
166
|
+
const paths = ['public/sitemap.xml', 'sitemap.xml', 'src/pages/sitemap.xml.ts'];
|
|
167
|
+
for (const p of paths) {
|
|
168
|
+
if (fs.existsSync(path.join(projectDir, p)))
|
|
169
|
+
return p;
|
|
170
|
+
}
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
findLlmsTxt(projectDir) {
|
|
174
|
+
const paths = ['public/llms.txt', 'llms.txt'];
|
|
175
|
+
for (const p of paths) {
|
|
176
|
+
if (fs.existsSync(path.join(projectDir, p)))
|
|
177
|
+
return p;
|
|
178
|
+
}
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
extractDynamicParams(routePath) {
|
|
182
|
+
const params = [];
|
|
183
|
+
const matches = routePath.match(/\[\.{0,3}([a-zA-Z0-9_-]+)\]/g);
|
|
184
|
+
if (matches) {
|
|
185
|
+
for (const m of matches) {
|
|
186
|
+
params.push(m.replace(/[\[\].]/g, ''));
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return params;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { FrameworkAdapter, MetadataLocationInfo, CanonicalLocationInfo, SchemaLocationInfo } from './types.js';
|
|
2
|
+
import { DiscoveredRoute, ProjectFrameworkInfo, RouteSourceMapping } from '../types/findings.js';
|
|
3
|
+
export declare class NextAppAdapter implements FrameworkAdapter {
|
|
4
|
+
id: string;
|
|
5
|
+
name: string;
|
|
6
|
+
detect(projectDir: string): boolean;
|
|
7
|
+
getProjectInfo(projectDir: string): ProjectFrameworkInfo;
|
|
8
|
+
discoverRoutes(projectDir: string): DiscoveredRoute[];
|
|
9
|
+
mapRouteToSource(targetUrl: string, routes: DiscoveredRoute[]): RouteSourceMapping;
|
|
10
|
+
findMetadataImplementation(projectDir: string, route: DiscoveredRoute): Promise<MetadataLocationInfo>;
|
|
11
|
+
findCanonicalDeclaration(projectDir: string, route: DiscoveredRoute): Promise<CanonicalLocationInfo>;
|
|
12
|
+
findSchemaDeclaration(projectDir: string, route: DiscoveredRoute): Promise<SchemaLocationInfo>;
|
|
13
|
+
findRobotsConfig(projectDir: string): string | null;
|
|
14
|
+
findSitemapConfig(projectDir: string): string | null;
|
|
15
|
+
findLlmsTxt(projectDir: string): string | null;
|
|
16
|
+
private extractDynamicParams;
|
|
17
|
+
}
|