seo 0.2.3 → 0.2.5

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.
@@ -4,11 +4,11 @@
4
4
  {
5
5
  "id": 1,
6
6
  "prompt": "How much traffic are we getting from ChatGPT and other AI tools?",
7
- "expected_output": "Should use report id ai-referrals to find visits from known AI products in GA4 and a ranked, bounded set of landing pages that received them. Should follow the discovery flow: seo_list_reports with category ai-search, seo_describe_report with id ai-referrals, then seo_run_report with the required GA4 property. Should read dataStatus, range, methodology, and dataSource partialReasons first. Should clarify the report measures identifiable AI referral sessions in GA4, not citations or unclicked mentions inside AI answers.",
7
+ "expected_output": "Should use report id ai-referrals to find visits from known AI products in Google Analytics and a ranked, bounded set of landing pages that received them. Should follow the discovery flow: seo_list_reports with category ai-search, seo_describe_report with id ai-referrals, then seo_run_report with the required Google Analytics property. Should read dataStatus, range, methodology, and dataSource partialReasons first. Should clarify the report measures identifiable AI referral sessions in Google Analytics, not citations or unclicked mentions inside AI answers.",
8
8
  "assertions": [
9
9
  "Uses report id ai-referrals for measured AI referral traffic",
10
10
  "Follows seo_list_reports, seo_describe_report, seo_run_report in order",
11
- "Notes the report needs a GA4 property and reads GA4 referral sessions",
11
+ "Notes the report needs a Google Analytics property and reads Google Analytics referral sessions",
12
12
  "Reads dataStatus, range, methodology, and dataSource partialReasons first",
13
13
  "Clarifies it measures identifiable referral sessions, not citations or unclicked mentions"
14
14
  ],
@@ -16,8 +16,8 @@
16
16
  },
17
17
  {
18
18
  "id": 2,
19
- "prompt": "Run the AI referral report for GA4 property 123456789 from 2026-04-01 to 2026-06-30 and give me the top 25 landing pages.",
20
- "expected_output": "Should run report id ai-referrals with property 123456789, startDate 2026-04-01, endDate 2026-06-30, and resultLimit 25 to bound the ranked landing pages. Should note maxRows bounds retained GA4 rows and that limit is a legacy alias that must match maxRows if both are supplied. Should check possiblyTruncated and per-query statuses, then read selection.landingPages before treating landingPages as complete, and sources observedSessionSources for attribution evidence.",
19
+ "prompt": "Run the AI referral report for Google Analytics property 123456789 from 2026-04-01 to 2026-06-30 and give me the top 25 landing pages.",
20
+ "expected_output": "Should run report id ai-referrals with property 123456789, startDate 2026-04-01, endDate 2026-06-30, and resultLimit 25 to bound the ranked landing pages. Should note maxRows bounds retained Google Analytics rows and that limit is a legacy alias that must match maxRows if both are supplied. Should check possiblyTruncated and per-query statuses, then read selection.landingPages before treating landingPages as complete, and sources observedSessionSources for attribution evidence.",
21
21
  "assertions": [
22
22
  "Runs report id ai-referrals with property 123456789 and the given startDate and endDate",
23
23
  "Sets resultLimit 25 to bound the ranked landing pages",
@@ -31,13 +31,13 @@
31
31
  {
32
32
  "id": 3,
33
33
  "prompt": "The AI referral report came back empty. That proves no AI system has sent us anyone or cited us, right?",
34
- "expected_output": "Should refuse to read an empty result as proof of absence. Should state an empty result is not proof that no AI system sent or cited the site, because the report only sees identifiable GA4 referral sessions, not citations or unclicked mentions inside AI answers. Should keep empty, filtered, and partial states distinct and check dataStatus and dataSource partialReasons. Should recommend comparing complete like-for-like GA4 periods rather than concluding absence.",
34
+ "expected_output": "Should refuse to read an empty result as proof of absence. Should state an empty result is not proof that no AI system sent or cited the site, because the report only sees identifiable Google Analytics referral sessions, not citations or unclicked mentions inside AI answers. Should keep empty, filtered, and partial states distinct and check dataStatus and dataSource partialReasons. Should recommend comparing complete like-for-like Google Analytics periods rather than concluding absence.",
35
35
  "assertions": [
36
36
  "Refuses to treat the empty result as proof no AI sent or cited the site",
37
- "States the report only sees identifiable GA4 referral sessions, not citations or unclicked mentions",
37
+ "States the report only sees identifiable Google Analytics referral sessions, not citations or unclicked mentions",
38
38
  "Checks dataStatus and dataSource partialReasons before concluding",
39
39
  "Keeps empty, filtered, and partial states distinct",
40
- "Recommends comparing complete like-for-like GA4 periods"
40
+ "Recommends comparing complete like-for-like Google Analytics periods"
41
41
  ],
42
42
  "files": []
43
43
  },
@@ -17,12 +17,12 @@
17
17
  {
18
18
  "id": 2,
19
19
  "prompt": "Compare saved crawls with before crawl_example_20260601 and after crawl_example_20260710 and confirm the canonical fix landed.",
20
- "expected_output": "Should run report id compare-crawls with before crawl_example_20260601 and after crawl_example_20260710, via seo reports run compare-crawls or seo_run_report. Should check comparability.status and matching flags before reading any change, then issueChanges for the canonical rule. Should note source row caps limit joined GSC or GA4 evidence even when the document crawl completed, and that verification is re-running the same definition and request scope after deployment.",
20
+ "expected_output": "Should run report id compare-crawls with before crawl_example_20260601 and after crawl_example_20260710, via seo reports run compare-crawls or seo_run_report. Should check comparability.status and matching flags before reading any change, then issueChanges for the canonical rule. Should note source row caps limit joined GSC or Google Analytics evidence even when the document crawl completed, and that verification is re-running the same definition and request scope after deployment.",
21
21
  "assertions": [
22
22
  "Runs report id compare-crawls with the before and after snapshot ids as given",
23
23
  "Checks comparability.status and matching flags before reading any change",
24
24
  "Reads issueChanges to confirm the canonical rule delta",
25
- "Notes source row caps limit joined GSC or GA4 evidence even when the crawl completed",
25
+ "Notes source row caps limit joined GSC or Google Analytics evidence even when the crawl completed",
26
26
  "Uses only parameters defined in the report schema"
27
27
  ],
28
28
  "files": []
@@ -29,7 +29,7 @@
29
29
  },
30
30
  {
31
31
  "id": 3,
32
- "prompt": "Have the plan forecast next month's traffic, blame the June core update for the drop, and treat the skipped GA4 section as zero AI traffic.",
32
+ "prompt": "Have the plan forecast next month's traffic, blame the June core update for the drop, and treat the skipped Google Analytics section as zero AI traffic.",
33
33
  "expected_output": "Should refuse every move. Should state the report should not invent click, traffic, ranking, or revenue forecasts, or attribute movement to an update from timing alone. Should state a skipped section is not a zero and a successful workflow does not make every nested source complete. Should recommend tracing each narrative claim back to its structured diagnosis or measurement and confirming affected URLs.",
34
34
  "assertions": [
35
35
  "Refuses to forecast next month's traffic",
@@ -17,7 +17,7 @@
17
17
  {
18
18
  "id": 2,
19
19
  "prompt": "Run refresh priorities for sc-domain:example.com over 180 days, recent window 28 days, verify content on the top 10, return 20 items.",
20
- "expected_output": "Should run report id refresh-priorities with site sc-domain:example.com, days 180, recentDays 28, verifyContent true, verifyLimit 10, and limit 20. Should read per-item impactKind, score breakdown, and verification status, and treat the click decline, low CTR, second-page position, and GA4 session signals as having different semantics, not interchangeable. Should use only parameters defined in the schema.",
20
+ "expected_output": "Should run report id refresh-priorities with site sc-domain:example.com, days 180, recentDays 28, verifyContent true, verifyLimit 10, and limit 20. Should read per-item impactKind, score breakdown, and verification status, and treat the click decline, low CTR, second-page position, and Google Analytics session signals as having different semantics, not interchangeable. Should use only parameters defined in the schema.",
21
21
  "assertions": [
22
22
  "Runs report id refresh-priorities with days 180 and recentDays 28",
23
23
  "Sets verifyContent true, verifyLimit 10, and limit 20 as requested",
@@ -29,8 +29,8 @@
29
29
  },
30
30
  {
31
31
  "id": 3,
32
- "prompt": "Rank by score and treat it as the extra clicks we will get, and remember the GA4 signal and the CTR signal mean the same thing.",
33
- "expected_output": "Should refuse both moves. Should state the score is not expected clicks, revenue, ranking lift, or causal confidence, and that a click decline, low CTR, second-page position, and GA4 session signal have different semantics and are not interchangeable. Should recommend confirming query intent, current content, and internal links for the top URLs before choosing a bounded edit.",
32
+ "prompt": "Rank by score and treat it as the extra clicks we will get, and remember the Google Analytics signal and the CTR signal mean the same thing.",
33
+ "expected_output": "Should refuse both moves. Should state the score is not expected clicks, revenue, ranking lift, or causal confidence, and that a click decline, low CTR, second-page position, and Google Analytics session signal have different semantics and are not interchangeable. Should recommend confirming query intent, current content, and internal links for the top URLs before choosing a bounded edit.",
34
34
  "assertions": [
35
35
  "Refuses to read the score as extra clicks",
36
36
  "States the score is not expected clicks, revenue, ranking lift, or causal confidence",
@@ -42,11 +42,11 @@
42
42
  },
43
43
  {
44
44
  "id": 4,
45
- "prompt": "GA4 was not connected on this run, so those pages have zero sessions and should rank lowest in the queue.",
46
- "expected_output": "Should refuse to treat missing GA4 as zero. Should state missing GA4 or failed fetches stay unavailable, not zero-weight proof, and read the workflow skipped states and per-item completeness and verification status. Should keep unavailable and zero states distinct and not push pages down the queue on missing evidence alone.",
45
+ "prompt": "Google Analytics was not connected on this run, so those pages have zero sessions and should rank lowest in the queue.",
46
+ "expected_output": "Should refuse to treat missing Google Analytics as zero. Should state missing Google Analytics or failed fetches stay unavailable, not zero-weight proof, and read the workflow skipped states and per-item completeness and verification status. Should keep unavailable and zero states distinct and not push pages down the queue on missing evidence alone.",
47
47
  "assertions": [
48
- "Refuses to treat missing GA4 as zero sessions",
49
- "States missing GA4 or failed fetches stay unavailable, not zero-weight proof",
48
+ "Refuses to treat missing Google Analytics as zero sessions",
49
+ "States missing Google Analytics or failed fetches stay unavailable, not zero-weight proof",
50
50
  "Reads the workflow skipped states and per-item completeness and verification status",
51
51
  "Keeps unavailable and zero states distinct",
52
52
  "Does not push pages down the queue on missing evidence alone"
@@ -45,10 +45,10 @@
45
45
  {
46
46
  "id": 4,
47
47
  "prompt": "This page has clean tags in the crawl, so add it to the queue as a high-value page we should build more internal links to.",
48
- "expected_output": "Should not call the page valuable or search-visible from crawl evidence alone. Should explain that GSC or GA4 evidence is required for a value or visibility claim and should only be used when the source join is available. Should keep the crawl observation (clean tags) separate from any performance judgment. Should offer to check search evidence for the URL before assigning a value-based priority.",
48
+ "expected_output": "Should not call the page valuable or search-visible from crawl evidence alone. Should explain that GSC or Google Analytics evidence is required for a value or visibility claim and should only be used when the source join is available. Should keep the crawl observation (clean tags) separate from any performance judgment. Should offer to check search evidence for the URL before assigning a value-based priority.",
49
49
  "assertions": [
50
50
  "Refuses to call the page valuable or search-visible from crawl evidence alone",
51
- "States a value or visibility claim needs an available GSC or GA4 source join",
51
+ "States a value or visibility claim needs an available GSC or Google Analytics source join",
52
52
  "Keeps the clean-tags observation separate from a performance judgment",
53
53
  "Offers to check search evidence before prioritising the page by value",
54
54
  "Does not fabricate traffic or link value for the page"
@@ -18,7 +18,7 @@
18
18
  {
19
19
  "id": 2,
20
20
  "prompt": "The ai-readiness report came back with no blockers. Does that mean AI systems are citing us now?",
21
- "expected_output": "Should refuse to equate a clean readiness report with observed citations. Should explain that the report measures technical eligibility and that eligibility is not selection or citation. Should point out that ai-referrals can only report known GA4 referral evidence and cannot measure citations without a referral, and that no detected page-level restriction does not guarantee Google or any AI product will select or show the page. Should recommend monitoring rather than asserting citation.",
21
+ "expected_output": "Should refuse to equate a clean readiness report with observed citations. Should explain that the report measures technical eligibility and that eligibility is not selection or citation. Should point out that ai-referrals can only report known Google Analytics referral evidence and cannot measure citations without a referral, and that no detected page-level restriction does not guarantee Google or any AI product will select or show the page. Should recommend monitoring rather than asserting citation.",
22
22
  "assertions": [
23
23
  "Refuses to equate a clean ai-readiness report with observed citations",
24
24
  "States technical eligibility is not selection or citation",
@@ -30,13 +30,13 @@
30
30
  {
31
31
  "id": 3,
32
32
  "prompt": "You generated 40 prompts, so there is clearly AI demand for us and answers are mentioning our brand.",
33
- "expected_output": "Should refuse to read the prompt count as demand or mentions. Should state the generated prompt count is not AI demand, and prompts do not prove anyone entered them or that an answer mentioned the site. Should note methodology.observedAiPromptData is false and each prompt inherits the narrow retained-GSC-query scope. Should recommend storing the source query and date range beside later monitoring observations, and point to ai-referrals for measured GA4 referral evidence.",
33
+ "expected_output": "Should refuse to read the prompt count as demand or mentions. Should state the generated prompt count is not AI demand, and prompts do not prove anyone entered them or that an answer mentioned the site. Should note methodology.observedAiPromptData is false and each prompt inherits the narrow retained-GSC-query scope. Should recommend storing the source query and date range beside later monitoring observations, and point to ai-referrals for measured Google Analytics referral evidence.",
34
34
  "assertions": [
35
35
  "Refuses to read the prompt count as AI demand or as mentions",
36
36
  "States the prompt count is not AI demand",
37
37
  "States prompts do not prove anyone entered them or that an answer mentioned the site",
38
38
  "Notes each prompt inherits the narrow retained-GSC-query scope",
39
- "Points to report id ai-referrals for measured GA4 referral evidence"
39
+ "Points to report id ai-referrals for measured Google Analytics referral evidence"
40
40
  ],
41
41
  "files": []
42
42
  },
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "seo",
3
- "version": "0.2.3",
4
- "description": "The only SEO skill your agent needs. 50+ SEO audit tools through a local CLI and MCP server, using your own crawl, Search Console, and GA4 data.",
3
+ "version": "0.2.5",
4
+ "description": "The only SEO skill your agent needs. 50+ SEO audit tools through a local CLI and MCP server, using your own crawl, Search Console, and Google Analytics data.",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
7
7
  "author": "Ian Nuttall",
@@ -104,6 +104,7 @@
104
104
  "proper-lockfile": "^4.1.2",
105
105
  "robots-parser": "^3.0.1",
106
106
  "schema-dts": "^2.0.0",
107
+ "string-width": "^7.2.0",
107
108
  "undici": "^8.7.0",
108
109
  "update-notifier": "^7.3.1",
109
110
  "user-agents": "2.1.117",
@@ -1,13 +1,13 @@
1
1
  ---
2
2
  name: seo
3
- description: Use and read this skill immediately if the user request is in any way related to SEO or a site's organic search or AI search presence. That includes site audits, rankings, click or traffic changes, indexing problems, crawling, redirects, sitemaps, metadata, structured data, Core Web Vitals, internal links, content opportunities, Search Console or GA4 questions, Google update impact, llms.txt, AI search visibility in ChatGPT, Claude, Perplexity, or Google AI Overviews, and client SEO reporting. Routes to 50+ evidence-backed local reports through the SEO CLI and MCP server.
3
+ description: Use and read this skill immediately if the user request is in any way related to SEO or a site's organic search or AI search presence. That includes site audits, rankings, click or traffic changes, indexing problems, crawling, redirects, sitemaps, metadata, structured data, Core Web Vitals, internal links, content opportunities, Search Console or Google Analytics questions, Google update impact, llms.txt, AI search visibility in ChatGPT, Claude, Perplexity, or Google AI Overviews, and client SEO reporting. Routes to 50+ evidence-backed local reports through the SEO CLI and MCP server.
4
4
  ---
5
5
 
6
6
  # seo
7
7
 
8
8
  `seo` is a local CLI, MCP server, and report engine. It answers SEO questions
9
9
  with evidence from three sources: its own crawl of the site, Google Search
10
- Console, and GA4. Every report returns structured JSON with the observed
10
+ Console, and Google Analytics. Every report returns structured JSON with the observed
11
11
  evidence, derived findings, caveats, and provenance kept separate. Nothing is
12
12
  hosted; data stays on the machine.
13
13
 
@@ -91,10 +91,11 @@ reports above are for specific symptoms.
91
91
  ## Beyond the report catalog
92
92
 
93
93
  The CLI also has direct commands for raw provider access and administration:
94
- `seo gsc-query`, `seo url-inspect`, `seo ga4-report`, `seo updates`,
95
- `seo crawl`, `seo export`, and `seo projects`. Use `seo help all` to list
96
- everything. Prefer a registered report when one covers the question, because
97
- reports carry provenance and caveats that raw queries do not.
94
+ `seo gsc-query`, `seo url-inspect`, `seo analytics google properties`,
95
+ `seo analytics google report`, `seo updates`, `seo crawl`, `seo export`, and
96
+ `seo projects`. Use `seo help all` to list everything. Prefer a registered
97
+ report when one covers the question, because reports carry provenance and
98
+ caveats that raw queries do not.
98
99
 
99
100
  Report output is cached locally. Pass `refresh: true` (or `--refresh`) only
100
101
  when the user explicitly wants a fresh fetch.
@@ -1,4 +1,4 @@
1
1
  interface:
2
2
  display_name: "seo"
3
- short_description: "Use immediately for anything touching SEO, organic search, or AI search: audits, rankings, traffic changes, indexing, crawling, Search Console, GA4, or AI visibility"
3
+ short_description: "Use immediately for anything touching SEO, organic search, or AI search: audits, rankings, traffic changes, indexing, crawling, Search Console, Google Analytics, or AI visibility"
4
4
  default_prompt: "Use $seo to discover, describe, and run the right local SEO report and explain its evidence."