Skip to content

Commit e00441e

Browse files
Merge pull request #40 from codefrydev/RichPdf
Rich pdf
2 parents 49f14ca + 093f70e commit e00441e

199 files changed

Lines changed: 21340 additions & 274 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.coveragerc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ omit =
88
*/website_profiling/integrations/bing/*
99
*/website_profiling/integrations/crux/*
1010
*/website_profiling/integrations/serp/*
11+
*/website_profiling/integrations/ai_citations/*
1112
*/website_profiling/integrations/links/third_party_csv.py
1213
*/website_profiling/lighthouse/*
1314
*/website_profiling/reporting/*

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,5 @@ pipeline-config.txt
3131
.coverage
3232
.agents/
3333
skills-lock.json
34-
crawl_results.csv
34+
crawl_results.csv
35+
commit.*

AGENT.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ Developer reference for agents and contributors. User-facing overview: [README.m
4343
| Local analysis | `analysis/local.py`, `requirements.txt` |
4444
| AI insights (LLM) | `llm/enrich.py`, `llm/agent.py`, `llm_config.py`, `requirements.txt` |
4545
| Audit query tools (MCP + chat) | `tools/audit_tools/`, `mcp/server.py`, `mcp/http_server.py`, `commands/chat_cmd.py` |
46+
| Agent readiness checks | `tools/audit_tools/agent_readiness.py`, `tools/audit_tools/_aeo_helpers.py` |
4647
| Config / CLI | `config.py` (`load_config`, `load_config_from_db`), `cli.py`, `input.txt.example` |
4748
| UI pipeline schema | `web/src/lib/pipelineConfigSchema.ts` |
4849
| UI LLM schema | `web/src/lib/llmConfigSchema.ts` |

AGENTS.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Agent instructions — Site Audit (WebsiteProfiling)
2+
3+
> Developer reference for AI coding agents and contributors.
4+
5+
This file is the canonical entry point for agents. For full detail see [AGENT.md](AGENT.md).
6+
7+
**What it is:** Self-hosted SEO crawl and technical audit platform — `python -m src` from repo root. Stack: Python (crawl + analysis + MCP), Next.js (web UI), PostgreSQL.
8+
9+
**Key paths**
10+
11+
- `src/website_profiling/` — core Python package
12+
- `cli.py`, `config.py`, `crawl/`, `db/`, `reporting/`, `analysis/`, `llm/`, `tools/`
13+
- `web/` — Next.js frontend
14+
- `alembic/` — DB migrations
15+
- `docs/` — documentation index
16+
- `tests/` — pytest suite
17+
18+
**Run / dev**
19+
20+
```bash
21+
./local-run # Start Postgres (Docker) + Next.js
22+
./local-test # Run all three coverage gates
23+
python -m src # Run audit pipeline
24+
python -m website_profiling.mcp # Start MCP server (stdio)
25+
```
26+
27+
**MCP:** 340 read-only audit tools via Model Context Protocol. See [docs/MCP.md](docs/MCP.md).
28+
29+
**Edit targets**
30+
31+
| Task | Where |
32+
|------|-------|
33+
| Crawl | `src/website_profiling/crawl/` |
34+
| Report | `src/website_profiling/reporting/` |
35+
| GEO / AEO / Agent readiness | `src/website_profiling/tools/audit_tools/geo_tools.py`, `agent_readiness.py` |
36+
| DB schema | `alembic/versions/` |
37+
| UI | `web/src/views/`, `web/app/` |
38+
39+
**Common pitfalls:** See [AGENT.md](AGENT.md) for the full footguns checklist (React context, Python local imports, psycopg dict rows, coverage gates).

README.md

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,13 +58,13 @@ Site Audit focuses on **honest, self-hosted technical SEO**. It is not a drop-in
5858

5959
- **No live backlink index** — Backlink tools read **Google Search Console Links CSV imports** (and optional third-party CSV overlays). There is no Ahrefs, Semrush, Moz, or Majestic API integration.
6060
- **No daily rank tracking** — Keyword positions come from **GSC snapshots** on your connected property, not a proprietary SERP tracker or rank-history database.
61-
- **No live AI citation checks** — GEO/AEO tools use **on-site heuristics**; they do not query ChatGPT, Perplexity, or other AI search engines in real time.
61+
- **Live AI citation checks are opt-in** — GEO/AEO tools default to **on-site heuristics** (no API required). Optional live checks via `check_ai_citations_live` require a BYO API key (`PERPLEXITY_API_KEY`, `OPENAI_API_KEY`, etc.) and explicit `opt_in=true`; they are not called automatically.
6262
- **No third-party keyword volume APIs** — Keyword explorer uses **on-site frequency plus Search Console**; difficulty and SERP feature overlays are estimated unless you supply your own data.
6363
- **No managed cloud** — You run it (Docker or local dev). This repo is not a hosted multi-tenant SaaS.
6464
- **No substitute for Google access** — Search Console, Analytics, and Bing Webmaster require **your credentials**; missing or stale integrations show empty states with provenance labels, not fabricated metrics.
6565
- **Not a ranking guarantee** — Category scores (0–100) are **internal audit scores**, not Google rankings or predicted traffic impact.
6666

67-
**Planned extensions** (not yet shipped): full backlink index beyond GSC import, SERP rank tracking beyond GSC snapshots, and live AI citation APIs. See [docs/MCP.md](docs/MCP.md#future-pipeline-items).
67+
**Planned extensions** (not yet shipped): full backlink index beyond GSC import, SERP rank tracking beyond GSC snapshots. See [docs/MCP.md](docs/MCP.md#future-pipeline-items).
6868

6969
## Features
7070

@@ -207,6 +207,18 @@ CI also runs a **Docker** job (image build, browser pytest in container, compose
207207

208208
Connect Google Search Console and Analytics via **Integrations** (gear icon) in the application UI.
209209

210+
### Google Ads Keyword Planner (optional)
211+
212+
Adds official search volume and competition data from the Google Ads API to the Keywords explorer. Requires:
213+
214+
1. A [Google Ads developer token](https://developers.google.com/google-ads/api/docs/first-call/dev-token) (Basic access is sufficient for keyword research).
215+
2. A Google Ads manager account customer ID (login customer ID).
216+
3. An existing Google OAuth connection (via Integrations) — users must re-consent after the `adwords` scope is added.
217+
218+
In **Integrations → Google Ads Keyword Planner**, enter the developer token and login customer ID. Then enable `enable_google_keyword_planner` in audit settings.
219+
220+
The overlay enriches keywords that have no Search Console impressions with `planner_avg_monthly_searches` and `planner_competition`, labelled "Google Keyword Planner" to distinguish them from real GSC data. GSC-ranked keywords are never overwritten. Set `enable_keyword_forecast = true` to additionally attach click/conversion forecasts to the top 50 keywords.
221+
210222
### JavaScript crawl (optional)
211223

212224
In Audit settings, set **Crawl rendering** to `javascript` (always headless Chromium) or `auto` (static first, browser when SPA heuristics match). Requires Playwright from `requirements.txt` and Chromium on `PATH` or `CHROME_PATH` (included in Docker). The UI preflights via `GET /api/crawl/browser-status` before runs when JS or auto mode is selected.
@@ -224,7 +236,9 @@ Ask questions about audit data at [http://localhost:3000/chat](http://localhost:
224236
| **Groq** | API key in AI settings or `GROQ_API_KEY`; official Groq Python SDK; native tool calling with streaming. Default model `openai/gpt-oss-120b`. |
225237

226238

227-
The agent uses the same **340 read-only audit tools** as the MCP server ([docs/MCP.md](docs/MCP.md)), with **dynamic routing** (~45 tools per turn). Responses stream over SSE (`POST /api/chat`). Sessions persist per property (`chat_sessions` / `chat_messages`).
239+
The agent uses the same **342 read-only audit tools** as the MCP server ([docs/MCP.md](docs/MCP.md)), with **dynamic routing** (~45 tools per turn). Responses stream over SSE (`POST /api/chat`). Sessions persist per property (`chat_sessions` / `chat_messages`).
240+
241+
**Read-only SQL chat tool (opt-in):** Set `CHAT_SQL_TOOL_ENABLED=true` to expose `get_sql_schema` and `run_sql_query` to the LLM. The agent can then answer arbitrary data questions by generating and executing a single read-only SELECT. Queries are validated by a four-layer guard (regex pre-filter → `sqlglot` AST + table allowlist → `BEGIN TRANSACTION READ ONLY` → optional least-privilege DB role); DELETE/UPDATE/INSERT/DDL and non-allowlisted tables are always blocked. In multi-property deployments, scope-binding CTEs are automatically injected to enforce tenant isolation. See [docs/OPS.md](docs/OPS.md#read-only-sql-chat-tool) for setup including the recommended `audit_readonly` Postgres role and optional RLS configuration.
228242

229243
### Content studio (optional, Experimental)
230244

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
"""Add developer_token and login_customer_id to google_app_settings for Keyword Planner.
2+
3+
Revision ID: 021_google_ads_planner_settings
4+
Revises: 020_crawl_run_pause_state
5+
Create Date: 2026-06-19
6+
"""
7+
from alembic import op
8+
9+
revision = "021_google_ads_planner_settings"
10+
down_revision = "020_crawl_run_pause_state"
11+
branch_labels = None
12+
depends_on = None
13+
14+
15+
def upgrade() -> None:
16+
op.execute(
17+
"ALTER TABLE google_app_settings ADD COLUMN IF NOT EXISTS developer_token TEXT"
18+
)
19+
op.execute(
20+
"ALTER TABLE google_app_settings ADD COLUMN IF NOT EXISTS login_customer_id TEXT"
21+
)
22+
23+
24+
def downgrade() -> None:
25+
op.execute(
26+
"ALTER TABLE google_app_settings DROP COLUMN IF EXISTS developer_token"
27+
)
28+
op.execute(
29+
"ALTER TABLE google_app_settings DROP COLUMN IF EXISTS login_customer_id"
30+
)

alembic/versions/022_dashboards.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
"""Custom dashboards — property-scoped dashboard builder with JSONB layout.
2+
3+
Revision ID: 022_dashboards
4+
Revises: 021_google_ads_planner_settings
5+
Create Date: 2026-06-19
6+
"""
7+
from __future__ import annotations
8+
9+
from alembic import op
10+
11+
revision = "022_dashboards"
12+
down_revision = "021_google_ads_planner_settings"
13+
branch_labels = None
14+
depends_on = None
15+
16+
17+
def upgrade() -> None:
18+
op.execute("""
19+
CREATE TABLE dashboards (
20+
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
21+
property_id BIGINT NOT NULL REFERENCES properties(id) ON DELETE CASCADE,
22+
name TEXT NOT NULL DEFAULT 'Untitled dashboard',
23+
layout_json JSONB NOT NULL DEFAULT '{}'::jsonb,
24+
is_default BOOLEAN NOT NULL DEFAULT false,
25+
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
26+
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
27+
)
28+
""")
29+
op.execute("""
30+
CREATE INDEX dashboards_property_updated_idx
31+
ON dashboards(property_id, updated_at DESC)
32+
""")
33+
34+
35+
def downgrade() -> None:
36+
op.execute("DROP TABLE IF EXISTS dashboards")
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
"""Add crawl_page_markdown table for per-URL extracted markdown storage.
2+
3+
Revision ID: 023_crawl_page_markdown
4+
Revises: 022_dashboards
5+
"""
6+
from __future__ import annotations
7+
8+
from alembic import op
9+
10+
revision = "023_crawl_page_markdown"
11+
down_revision = "022_dashboards"
12+
branch_labels = None
13+
depends_on = None
14+
15+
16+
def upgrade() -> None:
17+
op.execute("""
18+
CREATE TABLE crawl_page_markdown (
19+
crawl_run_id BIGINT NOT NULL REFERENCES crawl_runs(id) ON DELETE CASCADE,
20+
url TEXT NOT NULL,
21+
property_id BIGINT REFERENCES properties(id) ON DELETE SET NULL,
22+
title TEXT,
23+
markdown TEXT NOT NULL,
24+
word_count INTEGER NOT NULL DEFAULT 0,
25+
strategy TEXT NOT NULL DEFAULT 'main_only',
26+
source_byte_length INTEGER NOT NULL DEFAULT 0,
27+
extracted_at TIMESTAMPTZ NOT NULL DEFAULT now(),
28+
PRIMARY KEY (crawl_run_id, url)
29+
)
30+
""")
31+
op.execute("""
32+
CREATE INDEX idx_crawl_page_markdown_run
33+
ON crawl_page_markdown (crawl_run_id)
34+
""")
35+
op.execute("""
36+
CREATE INDEX idx_crawl_page_markdown_property
37+
ON crawl_page_markdown (property_id)
38+
""")
39+
40+
41+
def downgrade() -> None:
42+
op.execute("DROP INDEX IF EXISTS idx_crawl_page_markdown_property")
43+
op.execute("DROP INDEX IF EXISTS idx_crawl_page_markdown_run")
44+
op.execute("DROP TABLE IF EXISTS crawl_page_markdown")

docs/GLOSSARY.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ This glossary maps agency-facing UI terms to internal keys, database tables, and
4242
| Moz / Majestic overlay | `third_party_overlays` on `gsc_links`, `/api/backlinks/third-party-import` | CSV export upload | Referring-domain comparison vs GSC sample |
4343
| Bing backlinks | `bing_backlinks`, Integrations sync | Bing Webmaster API (optional) | Secondary link source |
4444
| SERP competition overlay | `serp_estimated_competition` on keywords | SerpAPI (optional) | Estimated SERP difficulty |
45+
| Keyword Planner overlay | `planner_avg_monthly_searches`, `planner_competition`, `planner_competition_index`, `planner_provenance` on keyword rows | Google Ads API `KeywordPlanIdeaService` (optional; `enable_google_keyword_planner`) | Official market-level search volume + competition — does not overwrite GSC impressions |
46+
| Keyword Planner discovery | New keyword rows with `sources: ["planner"]` | `GenerateKeywordIdeas` | Brand-new keywords not yet in crawl or GSC |
47+
| Keyword Planner forecast | `planner_forecast_clicks`, `planner_forecast_conversions` on top rows | `GenerateKeywordForecastMetrics` v24 (`enable_keyword_forecast`) | Paid-campaign click/conversion forecast — clearly labelled, not organic traffic |
4548
| Scheduled audits | `properties.schedule_cron`, `/api/schedule/check` | Cron + pipeline spawn | Recurring site audit — see [OPS.md](OPS.md) |
4649
| Property alerts | `alert_webhook_url`, `/api/alerts/check` | Health snapshot rules | Operations notifications |
4750
| Content brief | Keywords Brief button, `/api/keywords/content-brief` | LLM or deterministic | Content planning |

docs/MCP.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,26 @@ Size-based tools require `probe_image_inventory=true` in pipeline config. Relate
288288

289289
`get_geo_readiness_score`, `get_aeo_content_signals_for_url`, `get_llms_txt_status`, `draft_llms_txt`, `get_faq_schema_coverage`, `list_pages_missing_faq_schema`, `get_eeat_signals_summary`, `get_internal_link_suggestions`, `check_ai_citation_presence`
290290

291+
### Agent documentation readiness (agentic-seo parity)
292+
293+
`get_agent_readiness_score` — 5-category composite score (0-100, A-F grade): discovery, content structure, token economics, capability signaling, UX bridge.
294+
295+
**Discovery:** `get_agents_md_status`, `get_skill_md_status`, `get_agent_permissions_status`
296+
297+
**Token economics:** `get_token_budget_summary`, `list_oversized_pages_for_agents`
298+
299+
**Content structure:** `get_content_structure_aeo_summary`, `get_markdown_availability_summary`, `list_pages_agent_unfriendly`
300+
301+
**UX bridge:** `get_copy_for_ai_signals`, `list_pages_missing_copy_for_ai`
302+
303+
**Generator:** `generate_agent_readiness_bundle` — draft AGENTS.md, skill.md, agent-permissions.json
304+
305+
**Example prompts:**
306+
- "Score this site's agent documentation readiness"
307+
- "Which pages are over the 8k token limit for AI agents?"
308+
- "Does this site have an AGENTS.md or skill.md?"
309+
- "Generate agent readiness files for my site"
310+
291311
### Integrations
292312

293313
`get_bing_index_status` (requires `bing_webmaster_api_key` in audit settings)
@@ -306,6 +326,8 @@ In-app chat uses **dynamic tool routing**: each turn loads Tier 0 router tools p
306326

307327
Responses stream over SSE via `POST /api/chat`. Sessions persist per property in `chat_sessions` and `chat_messages`.
308328

329+
**Optional crawl actions:** When **Allow chat to start crawls** is enabled under **Run audit → Settings → Content & AI → Chat agent**, the chat agent can guide crawl setup and call `prepare_audit_run` to show an in-chat confirm card. The user must authorize crawling and click **Run audit** — the agent never spawns jobs directly. MCP tools remain read-only; `prepare_audit_run` is chat-only and excluded from MCP bundles.
330+
309331
---
310332

311333
## Provider notes

0 commit comments

Comments
 (0)