seo 0.13.2 → 0.14.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/PRIVACY.md +37 -8
- package/dist/cli.js +84 -80
- package/dist/cli.js.map +1 -1
- package/dist/{dist-BBszRLUG.js → dist-4Yam8ps7.js} +1 -1
- package/dist/dist-B_2zww72.js +427 -0
- package/dist/dist-B_2zww72.js.map +1 -0
- package/dist/{esm-Dmtr8qrP.js → esm-BmuxgZyN.js} +95 -188
- package/dist/{esm-Dmtr8qrP.js.map → esm-BmuxgZyN.js.map} +1 -1
- package/dist/index.d.ts +92 -406
- package/dist/index.js +1558 -646
- package/dist/index.js.map +1 -1
- package/dist/mcp.d.ts +0 -14
- package/dist/mcp.js +1130 -889
- package/dist/mcp.js.map +1 -1
- package/dist/provider-sdk.d.ts +0 -2
- package/dist/provider-sdk.js +1 -2
- package/package.json +6 -5
- package/site-kit/README.md +13 -0
- package/site-kit/guide/build.md +70 -0
- package/site-kit/guide/data.md +49 -0
- package/site-kit/guide/golden-page.md +50 -0
- package/site-kit/guide/grow.md +58 -0
- package/site-kit/guide/launch.md +55 -0
- package/site-kit/guide/market.md +57 -0
- package/site-kit/guide/plan.md +49 -0
- package/site-kit/guide/scale.md +41 -0
- package/site-kit/guide/setup.md +59 -0
- package/site-kit/guide/writing.md +55 -0
- package/site-kit/starter/.github/workflows/refresh-data.yml +88 -0
- package/site-kit/starter/.node-version +1 -0
- package/site-kit/starter/AGENTS.md +65 -0
- package/site-kit/starter/README.md +11 -0
- package/site-kit/starter/astro.config.mjs +31 -0
- package/site-kit/starter/gitignore +7 -0
- package/site-kit/starter/llms.config.mjs +38 -0
- package/site-kit/starter/package.json +31 -0
- package/site-kit/starter/pnpm-workspace.yaml +5 -0
- package/site-kit/starter/public/_headers +5 -0
- package/site-kit/starter/public/favicon.svg +1 -0
- package/site-kit/starter/public/fonts/InterLatinVF.woff2 +0 -0
- package/site-kit/starter/public/fonts/JetBrainsMonoLatinVF.woff2 +0 -0
- package/site-kit/starter/public/fonts/LICENSE-inter.txt +93 -0
- package/site-kit/starter/public/fonts/LICENSE-jetbrains-mono.txt +93 -0
- package/site-kit/starter/scripts/deploy.mjs +60 -0
- package/site-kit/starter/scripts/markdown-rewrites.mjs +33 -0
- package/site-kit/starter/scripts/refresh-data.mjs +60 -0
- package/site-kit/starter/scripts/validate-data.mjs +47 -0
- package/site-kit/starter/site.config.json +9 -0
- package/site-kit/starter/src/components/Breadcrumbs.astro +27 -0
- package/site-kit/starter/src/components/Button.astro +46 -0
- package/site-kit/starter/src/components/Card.astro +54 -0
- package/site-kit/starter/src/components/CodeBlock.astro +62 -0
- package/site-kit/starter/src/components/FactTable.astro +45 -0
- package/site-kit/starter/src/components/FaqAccordion.astro +143 -0
- package/site-kit/starter/src/components/Footer.astro +34 -0
- package/site-kit/starter/src/components/FormInput.astro +18 -0
- package/site-kit/starter/src/components/FormSelect.astro +34 -0
- package/site-kit/starter/src/components/Header.astro +17 -0
- package/site-kit/starter/src/components/HugeIcon.astro +36 -0
- package/site-kit/starter/src/components/Logo.astro +21 -0
- package/site-kit/starter/src/components/PageHeader.astro +33 -0
- package/site-kit/starter/src/components/SectionHeading.astro +18 -0
- package/site-kit/starter/src/components/ui/button.ts +51 -0
- package/site-kit/starter/src/data/parks.json +106 -0
- package/site-kit/starter/src/data/sources.json +11 -0
- package/site-kit/starter/src/integrations/og-images.ts +49 -0
- package/site-kit/starter/src/integrations/sitemaps.ts +104 -0
- package/site-kit/starter/src/layouts/Base.astro +69 -0
- package/site-kit/starter/src/lib/cn.ts +5 -0
- package/site-kit/starter/src/lib/narrative.ts +115 -0
- package/site-kit/starter/src/lib/og/card.ts +85 -0
- package/site-kit/starter/src/lib/og/path.ts +14 -0
- package/site-kit/starter/src/lib/og/render.ts +30 -0
- package/site-kit/starter/src/lib/parks.ts +84 -0
- package/site-kit/starter/src/lib/seo.ts +97 -0
- package/site-kit/starter/src/pages/404.astro +30 -0
- package/site-kit/starter/src/pages/about.astro +39 -0
- package/site-kit/starter/src/pages/index.astro +52 -0
- package/site-kit/starter/src/pages/parks/[slug].astro +95 -0
- package/site-kit/starter/src/pages/regions/[region].astro +58 -0
- package/site-kit/starter/src/pages/robots.txt.ts +16 -0
- package/site-kit/starter/src/site.ts +29 -0
- package/site-kit/starter/src/styles/globals.css +561 -0
- package/site-kit/starter/tsconfig.json +5 -0
- package/site-kit/starter/wrangler.jsonc +16 -0
- package/skills/seo/SKILL.md +19 -1
- package/dist/dist-7TxFytBp.js +0 -420
- package/dist/dist-7TxFytBp.js.map +0 -1
package/dist/provider-sdk.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
|
|
2
|
-
//#region packages/core/src/provider-extensions/sdk.d.ts
|
|
3
2
|
export declare const SEO_PROVIDER_API_VERSION: 1;
|
|
4
3
|
export type SeoProviderApiVersion = typeof SEO_PROVIDER_API_VERSION;
|
|
5
4
|
export type SeoProviderKind = 'traffic-analytics' | 'search-results' | 'keyword-data' | 'domain-data' | 'link-data' | 'local-search' | 'ai-data' | 'other';
|
|
@@ -199,5 +198,4 @@ export interface SeoProviderHost {
|
|
|
199
198
|
export type SeoProviderActivate = (host: SeoProviderHost) => void | Promise<void>;
|
|
200
199
|
export type SeoAnalyticsConnection = SeoProviderConnection;
|
|
201
200
|
export type SeoAnalyticsLandingPageResult = SeoLandingPageVisitsResult;
|
|
202
|
-
//#endregion
|
|
203
201
|
//# sourceMappingURL=provider-sdk.d.ts.map
|
package/dist/provider-sdk.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { createRequire as __seoCreateRequire } from 'node:module'; const require = __seoCreateRequire(import.meta.url);
|
|
2
|
-
|
|
2
|
+
// packages/core/src/provider-extensions/sdk.ts
|
|
3
3
|
const SEO_PROVIDER_API_VERSION = 1;
|
|
4
|
-
//#endregion
|
|
5
4
|
export { SEO_PROVIDER_API_VERSION };
|
|
6
5
|
|
|
7
6
|
//# sourceMappingURL=provider-sdk.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "seo",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"mcpName": "dev.seoskill/seo",
|
|
5
5
|
"description": "The SEO command for AI agents: a free Google Search Console MCP server and CLI. Reads your real Search Console, Google Analytics and Bing data for traffic drops, quick wins, content decay, cannibalization and indexing, plus DataForSEO and Ahrefs keyword research.",
|
|
6
6
|
"type": "module",
|
|
@@ -57,6 +57,7 @@
|
|
|
57
57
|
"files": [
|
|
58
58
|
"dist",
|
|
59
59
|
"skills",
|
|
60
|
+
"site-kit",
|
|
60
61
|
"evals",
|
|
61
62
|
"PRIVACY.md",
|
|
62
63
|
"SECURITY.md",
|
|
@@ -117,7 +118,7 @@
|
|
|
117
118
|
"jsonc-parser": "^3.3.1",
|
|
118
119
|
"libsql": "^0.5.29",
|
|
119
120
|
"lighthouse": "13.5.0",
|
|
120
|
-
"linkedom": "^0.18.
|
|
121
|
+
"linkedom": "^0.18.14",
|
|
121
122
|
"open": "^11.0.4",
|
|
122
123
|
"p-queue": "^9.3.5",
|
|
123
124
|
"p-retry": "^8.0.1",
|
|
@@ -134,10 +135,10 @@
|
|
|
134
135
|
"zod": "^4.6.7"
|
|
135
136
|
},
|
|
136
137
|
"devDependencies": {
|
|
137
|
-
"@biomejs/biome": "^2.
|
|
138
|
+
"@biomejs/biome": "^2.6.2",
|
|
138
139
|
"@modelcontextprotocol/sdk": "^1.32.1",
|
|
139
|
-
"tsdown": "^0.23.
|
|
140
|
-
"turbo": "^2.11.
|
|
140
|
+
"tsdown": "^0.23.2",
|
|
141
|
+
"turbo": "^2.11.10",
|
|
141
142
|
"typescript": "^6.0.3"
|
|
142
143
|
}
|
|
143
144
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Site kit
|
|
2
|
+
|
|
3
|
+
The files behind `seo site`. They ship in the npm package.
|
|
4
|
+
|
|
5
|
+
- `starter/` is the Astro site on Cloudflare that `seo site new <folder>`
|
|
6
|
+
copies. `gitignore` becomes `.gitignore` in the copy, because npm drops
|
|
7
|
+
`.gitignore` files from packages.
|
|
8
|
+
- `guide/` holds one Markdown file per step that `seo site guide <step>`
|
|
9
|
+
prints. The list and order of steps is `SITE_GUIDE_STEPS` in
|
|
10
|
+
`packages/cli/src/commands/site.ts`.
|
|
11
|
+
|
|
12
|
+
`seo site check` lives in `packages/core/src/site-check`. Change the starter
|
|
13
|
+
and the check together: the starter's own build must pass the check.
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Step 5 of 10: build
|
|
2
|
+
|
|
3
|
+
Goal: the site runs from the starter, deploys to a private preview on
|
|
4
|
+
Cloudflare and passes `seo site check`.
|
|
5
|
+
|
|
6
|
+
## Start the site
|
|
7
|
+
|
|
8
|
+
If the project does not exist yet, create it in a new folder:
|
|
9
|
+
|
|
10
|
+
```sh
|
|
11
|
+
seo site new <folder> --name "<Site name>" --domain <domain>
|
|
12
|
+
cd <folder>
|
|
13
|
+
pnpm build
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
The starter is a static Astro site on Cloudflare Workers static assets. It
|
|
17
|
+
already does the parts every page needs:
|
|
18
|
+
|
|
19
|
+
- One layout (`src/layouts/Base.astro`) writes the title, description,
|
|
20
|
+
canonical, sharing tags and one JSON-LD graph per page.
|
|
21
|
+
- A Markdown copy of every page, `llms.txt` and `agent-routes.json`, from
|
|
22
|
+
`agentMarkdown()`.
|
|
23
|
+
- A sharing image for every page, drawn at build time from its H1.
|
|
24
|
+
- A sitemap index with files of up to 5,000 URLs, listed in `robots.txt`.
|
|
25
|
+
- `robots.txt` open to every crawler, with a Content-Signal line. Set
|
|
26
|
+
`aiTraining` in `site.config.json` to the person's choice.
|
|
27
|
+
- `pnpm build` ends with `seo site check`, so a broken build never ships.
|
|
28
|
+
|
|
29
|
+
## Use the components
|
|
30
|
+
|
|
31
|
+
The starter has a clean blank theme for the person to make their own.
|
|
32
|
+
`AGENTS.md` lists the components: header, footer, page header,
|
|
33
|
+
breadcrumbs, section heading, cards with a destination label, fact table,
|
|
34
|
+
FAQ accordion, buttons, form inputs, code block and `site-prose` text.
|
|
35
|
+
Build every page from them. Make a new component only for something none
|
|
36
|
+
of them shows, in the same style: token classes from
|
|
37
|
+
`src/styles/globals.css`, muted panels, no borders, shadows or raw
|
|
38
|
+
colours. Change the look by changing the tokens, never page by page.
|
|
39
|
+
|
|
40
|
+
## Deploy a private preview
|
|
41
|
+
|
|
42
|
+
1. Create a GitHub repository and push: `gh repo create --private --source
|
|
43
|
+
. --push`.
|
|
44
|
+
2. In the Cloudflare dashboard, open Workers & Pages, create an
|
|
45
|
+
application, import the repository and keep the build command
|
|
46
|
+
`pnpm build`. Set the deploy command to `pnpm run deploy`.
|
|
47
|
+
3. Until launch the site lives on its workers.dev address. Protect it with
|
|
48
|
+
Cloudflare Access (in the Worker's settings, under Domains & Routes,
|
|
49
|
+
turn on Cloudflare Access for workers.dev) so only the person can open
|
|
50
|
+
it. Never use noindex for this: it is too easy to forget.
|
|
51
|
+
4. Send the person the preview link and a screenshot of the home page on
|
|
52
|
+
desktop and phone.
|
|
53
|
+
|
|
54
|
+
## Rules
|
|
55
|
+
|
|
56
|
+
- Static first. Run code on the server only for a route that needs it,
|
|
57
|
+
such as a search box over a large dataset, and keep it under `/api/*`.
|
|
58
|
+
Never set `run_worker_first` for anything else.
|
|
59
|
+
- Every word of content and the H1 is in the built HTML. Most AI systems
|
|
60
|
+
do not run JavaScript.
|
|
61
|
+
- One URL form everywhere: no trailing slash, and canonicals drop query
|
|
62
|
+
strings.
|
|
63
|
+
- Missing pages return a real 404. Never redirect them to the home page.
|
|
64
|
+
- Keep the data in committed JSON or R2. Never query a database on a page
|
|
65
|
+
request.
|
|
66
|
+
|
|
67
|
+
Done when: the preview is live behind Cloudflare Access, `pnpm build`
|
|
68
|
+
passes and the person has seen the screenshots.
|
|
69
|
+
|
|
70
|
+
Next: `seo site guide golden-page`
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Step 3 of 10: data
|
|
2
|
+
|
|
3
|
+
Goal: a clean dataset that is hard to copy, with a source and a checked
|
|
4
|
+
date for every fact, rebuilt from scratch by one command.
|
|
5
|
+
|
|
6
|
+
The data is what makes the site worth visiting. A site built on one copied
|
|
7
|
+
dataset gets no traction. Data that is combined, cleaned and worked out
|
|
8
|
+
into something new does.
|
|
9
|
+
|
|
10
|
+
## What the agent does
|
|
11
|
+
|
|
12
|
+
1. Find sources in this order: government and open data, then public pages
|
|
13
|
+
that can be collected, then data people add themselves. Check each
|
|
14
|
+
source's licence and terms before using it.
|
|
15
|
+
2. Write the import as a script that gives the same result every run, such
|
|
16
|
+
as `scripts/build-data.mjs`, from raw files in `data/raw` to JSON in
|
|
17
|
+
`src/data`. It reports any name it cannot match instead of guessing.
|
|
18
|
+
3. Join and enrich the sources: clean names, units and plurals, add places
|
|
19
|
+
and work out comparisons or a score. Formatting is a quality signal, so
|
|
20
|
+
"LLC" never becomes "Llc".
|
|
21
|
+
4. List every source in `src/data/sources.json` with its URL and the date
|
|
22
|
+
it was checked. `pnpm data:refresh` records each source's ETag, date and
|
|
23
|
+
hash, so a change opens a pull request.
|
|
24
|
+
5. Add a rule to `scripts/validate-data.mjs` for each kind of mistake the
|
|
25
|
+
data can contain, and run `pnpm data:validate`.
|
|
26
|
+
6. Replace the example parks data and the parks pages as the plan says.
|
|
27
|
+
|
|
28
|
+
## What the person does
|
|
29
|
+
|
|
30
|
+
- Decide how often the data is refreshed. Ask how often it changes at the
|
|
31
|
+
source before anything is built: daily for tides, yearly for statistics.
|
|
32
|
+
Set the cron in `.github/workflows/refresh-data.yml` to match.
|
|
33
|
+
- Spot-check 20 rows like an expert in the subject.
|
|
34
|
+
|
|
35
|
+
## Rules
|
|
36
|
+
|
|
37
|
+
- Keep an unknown value unknown. Never guess opening hours, rules or
|
|
38
|
+
prices. Store facts and work totals out from them.
|
|
39
|
+
- Never use a competitor as the source.
|
|
40
|
+
- Money and health numbers change only through a pull request a person
|
|
41
|
+
reviews. The refresh workflow does this for you.
|
|
42
|
+
- A score or index the site owns, explained on a methodology page, makes
|
|
43
|
+
pages worth linking to.
|
|
44
|
+
|
|
45
|
+
Done when: the dataset builds from nothing with one command, every fact
|
|
46
|
+
has a source and a checked date, `pnpm data:validate` passes and the
|
|
47
|
+
refresh workflow is scheduled.
|
|
48
|
+
|
|
49
|
+
Next: `seo site guide plan`
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Step 6 of 10: golden page
|
|
2
|
+
|
|
3
|
+
Goal: one page that is the best answer on the web for its search, built
|
|
4
|
+
from real data before any template exists.
|
|
5
|
+
|
|
6
|
+
## What the agent does
|
|
7
|
+
|
|
8
|
+
1. Build the first page of the main page type with real data, in the page
|
|
9
|
+
template the plan names. The starter's park page
|
|
10
|
+
(`src/pages/parks/[slug].astro`) shows the shape.
|
|
11
|
+
2. Take screenshots on desktop, at phone width and in dark mode. Review
|
|
12
|
+
them against the rules below before the person sees them.
|
|
13
|
+
3. Show the person the screenshots and the preview link, then change the
|
|
14
|
+
page from their feedback until they approve it.
|
|
15
|
+
|
|
16
|
+
## What the person does
|
|
17
|
+
|
|
18
|
+
Judge the page as a user, not as a builder. Do I know what this is at a
|
|
19
|
+
glance? Does it answer my question? Is every part there for a reason?
|
|
20
|
+
Would I send it to a friend who asked?
|
|
21
|
+
|
|
22
|
+
## How a good page is laid out
|
|
23
|
+
|
|
24
|
+
- A clear H1 and a short answer at the top that makes sense on its own.
|
|
25
|
+
Never "see below".
|
|
26
|
+
- The tool or the main data next, then a well designed table of the
|
|
27
|
+
details.
|
|
28
|
+
- A fact block for each set of facts, with the source and the checked date
|
|
29
|
+
(`src/components/FactTable.astro`).
|
|
30
|
+
- Questions people really ask, answered on the page. The FAQ markup
|
|
31
|
+
repeats them word for word, which `seo site check` tests.
|
|
32
|
+
- Links to related pages in the body, with words that name the page they
|
|
33
|
+
lead to. One link per target per page, never in the H1.
|
|
34
|
+
- Assumptions and caveats at the bottom, in plain words.
|
|
35
|
+
|
|
36
|
+
Charts, maps and calculators earn their place when they help. Every chart
|
|
37
|
+
also has its data in a table and a sentence that says what it shows.
|
|
38
|
+
|
|
39
|
+
## Design rules
|
|
40
|
+
|
|
41
|
+
Simple and quiet. The system font or Inter, plenty of space, one link
|
|
42
|
+
colour. `seo site check` fails the build on uppercase or letter-spaced
|
|
43
|
+
labels, gradients, shadows, coloured left borders and card grids wider
|
|
44
|
+
than two columns. Also avoid stat boxes, eyebrow labels above headings and
|
|
45
|
+
slogans made of three short phrases. Attention to detail is what
|
|
46
|
+
separates a good page: capitals, units, plurals and empty states.
|
|
47
|
+
|
|
48
|
+
Done when: the person approves the page and `pnpm build` passes.
|
|
49
|
+
|
|
50
|
+
Next: `seo site guide writing`
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Step 10 of 10: grow
|
|
2
|
+
|
|
3
|
+
Goal: know what is working from the site's own data, improve one thing at
|
|
4
|
+
a time, and keep the data current with minutes of the person's time a
|
|
5
|
+
week.
|
|
6
|
+
|
|
7
|
+
## Measure every week
|
|
8
|
+
|
|
9
|
+
Search Console data starts a few days after launch. Run the report each
|
|
10
|
+
week and treat each section as a list to review, never a forecast:
|
|
11
|
+
|
|
12
|
+
```sh
|
|
13
|
+
seo report --site <domain>
|
|
14
|
+
seo report --site <domain> --section quick-wins
|
|
15
|
+
seo report --site <domain> --section striking-distance
|
|
16
|
+
seo report --site <domain> --section ai-queries
|
|
17
|
+
seo index --site <domain>
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
- Quick wins and low CTR: pages near the top with fewer clicks than their
|
|
21
|
+
position should get. Review the title and description.
|
|
22
|
+
- Striking distance and second page: pages close to page one. Improve the
|
|
23
|
+
section that answers the search.
|
|
24
|
+
- AI queries: long questions people ask, which may need a section or an
|
|
25
|
+
FAQ answer.
|
|
26
|
+
- Content decay and traffic drop: what lost clicks, and the Google updates
|
|
27
|
+
around that date.
|
|
28
|
+
- `seo index`: why Google left pages out of its index.
|
|
29
|
+
|
|
30
|
+
Use `seo page --url <url>` for one page's searches and status. Your own
|
|
31
|
+
Search Console data is the only data that is about your site. Keyword
|
|
32
|
+
tools are estimates.
|
|
33
|
+
|
|
34
|
+
## Change one thing at a time
|
|
35
|
+
|
|
36
|
+
Note the date and what changed. Compare equal windows four and eight weeks
|
|
37
|
+
later with `seo compare --site <domain> --date <date>`. A change in the
|
|
38
|
+
same week as a Google update proves nothing on its own.
|
|
39
|
+
|
|
40
|
+
## Keep the data current
|
|
41
|
+
|
|
42
|
+
- The refresh workflow opens a pull request when a source changes. The
|
|
43
|
+
person checks the numbers and merges it.
|
|
44
|
+
- When a refresh fails, find out why and fix the import, then let the next
|
|
45
|
+
run open the pull request.
|
|
46
|
+
|
|
47
|
+
## Become known
|
|
48
|
+
|
|
49
|
+
Keep the name and description the same on the site, in its JSON-LD and on
|
|
50
|
+
every profile. Publish the site's own data and statistics, because those
|
|
51
|
+
are what people link to and AI answers cite. Never buy links or post fake
|
|
52
|
+
mentions.
|
|
53
|
+
|
|
54
|
+
At six months, a steady few hundred visits a day means keep going. Much
|
|
55
|
+
less means rebuild it smaller and better, or try the next idea.
|
|
56
|
+
|
|
57
|
+
Done when: the weekly report runs, and each item has an action or a clear
|
|
58
|
+
"no action".
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Step 9 of 10: launch
|
|
2
|
+
|
|
3
|
+
Goal: the site is live on its domain, Google and Bing know about it, and
|
|
4
|
+
the most important pages are found quickly.
|
|
5
|
+
|
|
6
|
+
## What the agent does
|
|
7
|
+
|
|
8
|
+
1. Add the domain to `wrangler.jsonc` and push:
|
|
9
|
+
|
|
10
|
+
```jsonc
|
|
11
|
+
"routes": [{ "pattern": "<domain>", "custom_domain": true }]
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Redirect `www` to the bare domain with a Cloudflare redirect rule.
|
|
15
|
+
Take Cloudflare Access off the live domain.
|
|
16
|
+
2. Add the two Markdown rewrite rules, so an agent that asks for
|
|
17
|
+
`Accept: text/markdown` at a normal URL gets the Markdown copy. Print
|
|
18
|
+
them with `pnpm rewrites` and add them in the Cloudflare dashboard under
|
|
19
|
+
Rules, URL rewrite. They run before the site, so no code runs per
|
|
20
|
+
request.
|
|
21
|
+
3. Check production: the home page, a page of each type, `robots.txt`,
|
|
22
|
+
`sitemap.xml`, `llms.txt`, a sharing image, a 404 and a Markdown
|
|
23
|
+
request:
|
|
24
|
+
|
|
25
|
+
```sh
|
|
26
|
+
seo crawl --url https://<domain>
|
|
27
|
+
curl -sI -H "Accept: text/markdown" https://<domain>/
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
4. From now on `pnpm run deploy` (the Cloudflare deploy command) sends the
|
|
31
|
+
pages that changed to IndexNow after each deploy.
|
|
32
|
+
5. Pick the 100 most important URLs: hubs, the pages with the most data
|
|
33
|
+
and the pages that link to everything else. Check each one is built,
|
|
34
|
+
canonical and in a sitemap.
|
|
35
|
+
|
|
36
|
+
## What the person does
|
|
37
|
+
|
|
38
|
+
1. Add the domain to Google Search Console as a Domain property and
|
|
39
|
+
verify it through Cloudflare. Submit `https://<domain>/sitemap.xml`.
|
|
40
|
+
2. Import the site into Bing Webmaster Tools from Search Console.
|
|
41
|
+
3. Add Clicky, on the production domain only.
|
|
42
|
+
4. Run `seo start` again if the new property should show up in SEO Skill.
|
|
43
|
+
|
|
44
|
+
## Rules
|
|
45
|
+
|
|
46
|
+
- Publish every page at once. A site built for people does not drip feed
|
|
47
|
+
pages.
|
|
48
|
+
- Found is not indexed. Expect impressions within days if the site is
|
|
49
|
+
good, and judge it at six months.
|
|
50
|
+
- Never change a live URL without a 301 to the closest page.
|
|
51
|
+
|
|
52
|
+
Done when: the domain serves the site, Search Console and Bing have the
|
|
53
|
+
sitemap, and `seo crawl` reports no errors.
|
|
54
|
+
|
|
55
|
+
Next: `seo site guide grow`
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Step 2 of 10: market
|
|
2
|
+
|
|
3
|
+
Goal: a niche with real search demand and data the site can own, where the
|
|
4
|
+
pages that rank today are weak. The step ends with a plan file and a go or
|
|
5
|
+
no-go from the person.
|
|
6
|
+
|
|
7
|
+
## What the agent does
|
|
8
|
+
|
|
9
|
+
1. Ask the person for the idea, in their words. Good ideas come from their
|
|
10
|
+
own life and hobbies, open data, forums, and sites that look weak.
|
|
11
|
+
2. Find the head term and its modifiers. Searches usually take the shape
|
|
12
|
+
head term plus modifier, such as "{club} {season} home shirt".
|
|
13
|
+
3. Read the live search results for each pattern: who ranks, what kind of
|
|
14
|
+
page wins (tool, list, directory item, guide) and how strong those sites
|
|
15
|
+
look.
|
|
16
|
+
4. With a keyword data key, get the numbers:
|
|
17
|
+
|
|
18
|
+
```sh
|
|
19
|
+
seo research keyword_metrics --keywords "<term>,<term>"
|
|
20
|
+
seo research serp --keywords "<head term>"
|
|
21
|
+
seo research competitors --target <a competitor domain>
|
|
22
|
+
seo research ranked_keywords --target <a competitor domain>
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Then look at the competitors of those competitors for pockets of
|
|
26
|
+
demand. Without a key, use the search results, autocomplete and "people
|
|
27
|
+
also ask", and say that the volumes are unknown.
|
|
28
|
+
5. Write `plan.md` in the project with the head terms, the modifiers, who
|
|
29
|
+
ranks, the data sources, how often the data changes, the page types and
|
|
30
|
+
a page count estimate.
|
|
31
|
+
|
|
32
|
+
## What the person does
|
|
33
|
+
|
|
34
|
+
Read `plan.md` and say go or no-go. Their judgement of the market matters
|
|
35
|
+
more than any number.
|
|
36
|
+
|
|
37
|
+
## Signs a market is winnable
|
|
38
|
+
|
|
39
|
+
- Forum threads, thin directories or old pages in the top results.
|
|
40
|
+
- Sites with few links and low authority ranking on page one.
|
|
41
|
+
- Searches with buyers behind them. 100 searches from people about to buy
|
|
42
|
+
beat 4,400 from people who want something free. A real cost per click
|
|
43
|
+
shows the market pays.
|
|
44
|
+
|
|
45
|
+
## Walk away when
|
|
46
|
+
|
|
47
|
+
- The data is too hard to get, too hard to keep current or too costly to
|
|
48
|
+
host.
|
|
49
|
+
- Every result is a household brand with years of links.
|
|
50
|
+
- The only pages you could make are thin rewrites of one source.
|
|
51
|
+
|
|
52
|
+
Build five ideas to find one that works. A quiet, boring market is fine.
|
|
53
|
+
|
|
54
|
+
Done when: `plan.md` names the head terms, the page types, the data
|
|
55
|
+
sources and a page count, and the person said go.
|
|
56
|
+
|
|
57
|
+
Next: `seo site guide data`
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Step 4 of 10: plan
|
|
2
|
+
|
|
3
|
+
Goal: a page list where every page has real demand and one clear job, and
|
|
4
|
+
the domain to put it on.
|
|
5
|
+
|
|
6
|
+
## What the agent does
|
|
7
|
+
|
|
8
|
+
1. Map each search pattern to a page type and a URL pattern.
|
|
9
|
+
2. Check the demand for each page type before building it. With a keyword
|
|
10
|
+
data key, run `seo research keyword_metrics` on a sample of each
|
|
11
|
+
pattern. Without one, check that the search results show pages of that
|
|
12
|
+
type.
|
|
13
|
+
3. Group searches that share the same top results into one page, so pages
|
|
14
|
+
never compete with each other.
|
|
15
|
+
4. When the data allows two sizes, propose both (for example about 700
|
|
16
|
+
pages or about 9,000) and let the person choose by risk.
|
|
17
|
+
5. Add a route list to `plan.md`: page type, URL pattern, target search
|
|
18
|
+
pattern and expected count.
|
|
19
|
+
6. Suggest three domains: two words, no hyphens, about the subject. Check
|
|
20
|
+
they are free on Cloudflare Registrar.
|
|
21
|
+
|
|
22
|
+
## What the person does
|
|
23
|
+
|
|
24
|
+
Approve the page list and the URL pattern, then buy the domain on
|
|
25
|
+
Cloudflare Registrar. Then put the domain in `site.config.json`.
|
|
26
|
+
|
|
27
|
+
## Rules
|
|
28
|
+
|
|
29
|
+
- Use the smallest page type that fully answers the search: a tool, a
|
|
30
|
+
landing page, then a guide.
|
|
31
|
+
- Every search intent gets one URL. Never make a second page for the same
|
|
32
|
+
job.
|
|
33
|
+
- Small and focused wins: hundreds of rich pages, never tens of thousands
|
|
34
|
+
of thin ones. Directories are the exception, because people search for
|
|
35
|
+
each item by name.
|
|
36
|
+
- Long variants of a search become sections on one strong page. They do
|
|
37
|
+
not get thin pages of their own.
|
|
38
|
+
- Never make one page per question or per wording of a search. Google
|
|
39
|
+
treats that as scaled content abuse.
|
|
40
|
+
- Build a page only when the data has enough to make it useful. A URL that
|
|
41
|
+
does not qualify is never built.
|
|
42
|
+
- URLs are lowercase and lead with the subject, one or two folders deep,
|
|
43
|
+
with no IDs, no source names and no trailing slash.
|
|
44
|
+
- Hub pages are real pages with their own data, not lists of 500 links.
|
|
45
|
+
|
|
46
|
+
Done when: `plan.md` has the route list, the person approved it, and the
|
|
47
|
+
domain is bought and set in `site.config.json`.
|
|
48
|
+
|
|
49
|
+
Next: `seo site guide build`
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Step 8 of 10: scale
|
|
2
|
+
|
|
3
|
+
Goal: every page as good as the golden page.
|
|
4
|
+
|
|
5
|
+
## What the agent does
|
|
6
|
+
|
|
7
|
+
1. Turn the golden page into the template for its page type. Each block
|
|
8
|
+
shows only when the data has enough for it.
|
|
9
|
+
2. Build every page, then screenshot a sample of each page type: the
|
|
10
|
+
pages with the most data, the least data and the strangest data.
|
|
11
|
+
3. Review the sample with a second agent that did not build it.
|
|
12
|
+
4. Link the pages in the build: hubs to their pages, pages to their
|
|
13
|
+
siblings, and every page within three clicks of the home page.
|
|
14
|
+
`seo site check` fails on pages nothing links to.
|
|
15
|
+
5. Update `llms.config.mjs` so `llms.txt` lists the hubs and page types.
|
|
16
|
+
|
|
17
|
+
## What the person does
|
|
18
|
+
|
|
19
|
+
Review the sample from every page type, most of all the pages with the
|
|
20
|
+
least data. A thin page gets better data, gets merged into a stronger page
|
|
21
|
+
or is not built.
|
|
22
|
+
|
|
23
|
+
## Rules
|
|
24
|
+
|
|
25
|
+
- Fix a problem in the template or the data, never page by page. Fixing
|
|
26
|
+
pages one at a time hides the real fault.
|
|
27
|
+
- Reuse the starter's components. Do not invent a new one for each page
|
|
28
|
+
type.
|
|
29
|
+
- Rigid templates with thin data make bad pages. Leave a block out rather
|
|
30
|
+
than fill it with filler.
|
|
31
|
+
- `seo site check` checks every page before deploy: titles and
|
|
32
|
+
descriptions present, unique and the right length, one H1, canonicals,
|
|
33
|
+
JSON-LD, FAQ markup that matches the page, links that work, every page in
|
|
34
|
+
a sitemap, no noindex, a sharing image and a Markdown copy for each page,
|
|
35
|
+
the copy and design rules, and a source and date on every fact block.
|
|
36
|
+
It stops at 10,000 pages.
|
|
37
|
+
|
|
38
|
+
Done when: a random sample of pages passes the person's review and
|
|
39
|
+
`pnpm build` passes.
|
|
40
|
+
|
|
41
|
+
Next: `seo site guide launch`
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Step 1 of 10: setup
|
|
2
|
+
|
|
3
|
+
Goal: the person has every account the site needs, signed in on this
|
|
4
|
+
machine, and knows what each one costs.
|
|
5
|
+
|
|
6
|
+
Work through this with the person one account at a time. For each one, say
|
|
7
|
+
where to click, what it costs and what to paste back. Logins that open a
|
|
8
|
+
browser come before API keys. Never ask for a password, and never ask the
|
|
9
|
+
person to paste a key into the chat. Keys go into the tool that uses them.
|
|
10
|
+
|
|
11
|
+
## Check the machine
|
|
12
|
+
|
|
13
|
+
Check for Node.js 22.12 or newer, pnpm and git, and install what is
|
|
14
|
+
missing. Then install `seo`:
|
|
15
|
+
|
|
16
|
+
```sh
|
|
17
|
+
node --version
|
|
18
|
+
pnpm --version || npm i -g pnpm
|
|
19
|
+
git --version
|
|
20
|
+
npm i -g seo
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Sign in to each account, in this order
|
|
24
|
+
|
|
25
|
+
| Order | Account | How | Cost |
|
|
26
|
+
| --- | --- | --- | --- |
|
|
27
|
+
| 1 | GitHub | `gh auth login`, then choose the browser login | Free |
|
|
28
|
+
| 2 | Cloudflare | `npx wrangler login` | The free plan is enough |
|
|
29
|
+
| 3 | Domain | Buy it later, in the plan step, on Cloudflare Registrar | About $10 a year |
|
|
30
|
+
| 4 | Google Search Console | After the domain exists, add a Domain property and verify it through Cloudflare | Free |
|
|
31
|
+
| 5 | Bing Webmaster Tools | Import the site from Search Console | Free |
|
|
32
|
+
| 6 | SEO Skill | `seo start` signs in with Google and adds the MCP server and skill | Free |
|
|
33
|
+
| 7 | Keyword data (optional) | A DataForSEO or Ahrefs key, saved on the account page at https://seoskill.dev/account, then `seo login` | Free without a key, the provider bills you with one |
|
|
34
|
+
| 8 | Analytics | Clicky, on the production site only | Free tier |
|
|
35
|
+
|
|
36
|
+
Accounts 3 to 5 need the domain, so they wait until the plan is approved.
|
|
37
|
+
Note each one as a to-do for the launch step.
|
|
38
|
+
|
|
39
|
+
## Keyword data is the person's choice
|
|
40
|
+
|
|
41
|
+
Without a key, the market and plan steps use free sources: the live search
|
|
42
|
+
results, autocomplete, forums and, once the site is live, its own Search
|
|
43
|
+
Console data. With a DataForSEO or Ahrefs key, `seo research` adds search
|
|
44
|
+
volumes, difficulty, the pages that rank and competitor keywords. Explain
|
|
45
|
+
both, give the price page of each provider, and let the person choose.
|
|
46
|
+
DataForSEO bills per request. Ahrefs needs a plan that includes API units.
|
|
47
|
+
|
|
48
|
+
## Rules
|
|
49
|
+
|
|
50
|
+
- One account at a time. Wait for the person to finish before the next.
|
|
51
|
+
- If a login fails, read the error to them in plain words and try once more
|
|
52
|
+
before moving on.
|
|
53
|
+
- Write what was set up in the project notes, never the keys themselves.
|
|
54
|
+
|
|
55
|
+
Done when: `gh auth status`, `npx wrangler whoami` and `seo doctor` all
|
|
56
|
+
show a signed-in account, and the person has chosen free or paid keyword
|
|
57
|
+
data.
|
|
58
|
+
|
|
59
|
+
Next: `seo site guide market`
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Step 7 of 10: writing
|
|
2
|
+
|
|
3
|
+
Goal: copy a person enjoys reading and an AI answer can lift out and quote
|
|
4
|
+
correctly.
|
|
5
|
+
|
|
6
|
+
## What the agent does
|
|
7
|
+
|
|
8
|
+
1. Write in two passes. First collect the facts for a page from the data
|
|
9
|
+
and its sources. Then write only from those facts.
|
|
10
|
+
2. Put sentences that depend on the data in `src/lib/narrative.ts`. Each
|
|
11
|
+
sentence is shown only when its fact exists, and the wording changes
|
|
12
|
+
with the numbers, so pages read differently.
|
|
13
|
+
3. Check every claim against its source before it ships.
|
|
14
|
+
4. Run `pnpm build`. `seo site check` fails on the copy rules below.
|
|
15
|
+
|
|
16
|
+
## What the person does
|
|
17
|
+
|
|
18
|
+
Plan the sections of the main page type, because sections an AI plans come
|
|
19
|
+
out generic. Read a few pages like a visitor, and turn every pattern they
|
|
20
|
+
dislike into a rule.
|
|
21
|
+
|
|
22
|
+
## Rules for people
|
|
23
|
+
|
|
24
|
+
- Clear, direct, full sentences. Say what the reader gets.
|
|
25
|
+
- The length is whatever answers the question. No word count targets, and
|
|
26
|
+
no "what is X" opening when the reader already knows.
|
|
27
|
+
- The title tag is for the search result: the words people search for
|
|
28
|
+
first, short enough not to be cut off. The H1 says the same thing in
|
|
29
|
+
plainer words.
|
|
30
|
+
- Questions in an FAQ come from real searches or common sense.
|
|
31
|
+
- Never write about the build, the tools, collecting data, search terms or
|
|
32
|
+
"coming soon".
|
|
33
|
+
|
|
34
|
+
`seo site check` fails on: dashes used as punctuation, semicolons, "it's
|
|
35
|
+
not X, it's Y" lines, hype words such as seamless, robust and unlock,
|
|
36
|
+
openings such as "in this guide" and "did you know", tool and process
|
|
37
|
+
words, and "coming soon". When a banned word is part of the subject (a
|
|
38
|
+
site about Cloudflare, say), add it to `allowWords` in `site.config.json`.
|
|
39
|
+
|
|
40
|
+
## Rules for being quoted
|
|
41
|
+
|
|
42
|
+
- Every section answers on its own. Name the subject, give the answer in
|
|
43
|
+
the first sentence or two, then the conditions. Test it by reading the
|
|
44
|
+
section alone.
|
|
45
|
+
- Write H2s the way people ask.
|
|
46
|
+
- Keep each number next to its source and date, in visible text.
|
|
47
|
+
- Credit the site's own figures by name: "{Site}'s data shows", never "our
|
|
48
|
+
data".
|
|
49
|
+
- One strong page per claim. Keep price and other fast moving pages fresh,
|
|
50
|
+
with a visible date.
|
|
51
|
+
|
|
52
|
+
Done when: every section passes the read-it-alone test, the facts match the
|
|
53
|
+
sources and `pnpm build` passes.
|
|
54
|
+
|
|
55
|
+
Next: `seo site guide scale`
|