anymorph 0.4.0 → 0.6.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.
Files changed (76) hide show
  1. package/README.md +3 -3
  2. package/dist/index.js +79 -6
  3. package/package.json +1 -1
  4. package/dist/skillpacks/geo/scaffold/AGENTS.md +0 -38
  5. package/dist/skillpacks/geo/scaffold/CLAUDE.md +0 -33
  6. package/dist/skillpacks/geo/shared/evidence-principles.md +0 -35
  7. package/dist/skillpacks/geo/shared/geo-principles.md +0 -281
  8. package/dist/skillpacks/geo/shared/vertical-playbooks/beauty.md +0 -65
  9. package/dist/skillpacks/geo/shared/vertical-playbooks/commerce.md +0 -65
  10. package/dist/skillpacks/geo/shared/vertical-playbooks/ota.md +0 -62
  11. package/dist/skillpacks/geo/shared/vertical-playbooks/saas.md +0 -64
  12. package/dist/skillpacks/geo/skills/brand-owned-diagnosis/SKILL.md +0 -54
  13. package/dist/skillpacks/geo/skills/brand-owned-diagnosis/agents/openai.yaml +0 -4
  14. package/dist/skillpacks/geo/skills/brand-owned-diagnosis/references/diagnosis-contract.md +0 -95
  15. package/dist/skillpacks/geo/skills/brand-owned-diagnosis/references/workflow.md +0 -194
  16. package/dist/skillpacks/geo/skills/geo-generating-actions/SKILL.md +0 -188
  17. package/dist/skillpacks/geo/skills/geo-generating-actions/agents/openai.yaml +0 -4
  18. package/dist/skillpacks/geo/skills/geo-generating-actions/references/orchestrator.workflow.md +0 -440
  19. package/dist/skillpacks/geo/skills/geo-generating-actions/scripts/geo-scaffold.mjs +0 -358
  20. package/dist/skillpacks/geo/skills/geo-generating-actions/scripts/geo.mjs +0 -66
  21. package/dist/skillpacks/geo/skills/geo-initializing-strategy/SKILL.md +0 -50
  22. package/dist/skillpacks/geo/skills/geo-initializing-strategy/agents/openai.yaml +0 -4
  23. package/dist/skillpacks/geo/skills/geo-initializing-strategy/references/external-authority-diagnosis.md +0 -66
  24. package/dist/skillpacks/geo/skills/geo-initializing-strategy/references/foundation-diagnosis.md +0 -86
  25. package/dist/skillpacks/geo/skills/geo-initializing-strategy/references/memory-contract.md +0 -15
  26. package/dist/skillpacks/geo/skills/geo-initializing-strategy/references/semantic-clusters-diagnosis.md +0 -58
  27. package/dist/skillpacks/geo/skills/geo-initializing-strategy/references/strategy-map-contract.md +0 -26
  28. package/dist/skillpacks/geo/skills/geo-initializing-strategy/references/visibility-diagnosis.md +0 -50
  29. package/dist/skillpacks/geo/skills/geo-local-setup/SKILL.md +0 -66
  30. package/dist/skillpacks/geo/skills/geo-local-setup/agents/openai.yaml +0 -4
  31. package/dist/skillpacks/geo/skills/geo-page-writer/SKILL.md +0 -81
  32. package/dist/skillpacks/geo/skills/geo-page-writer/agents/openai.yaml +0 -4
  33. package/dist/skillpacks/geo/skills/geo-page-writer/references/research.md +0 -61
  34. package/dist/skillpacks/geo/skills/geo-page-writer/references/validation.md +0 -59
  35. package/dist/skillpacks/geo/skills/geo-page-writer/references/writing.md +0 -55
  36. package/dist/skillpacks/geo/skills/geo-page-writer/scripts/check-page-mdx.mjs +0 -210
  37. package/dist/skillpacks/geo/skills/geo-page-writer/scripts/collect-page-sources.mjs +0 -303
  38. package/dist/skillpacks/geo/skills/geo-pages-diagnosis/SKILL.md +0 -51
  39. package/dist/skillpacks/geo/skills/geo-pages-diagnosis/agents/openai.yaml +0 -4
  40. package/dist/skillpacks/geo/skills/geo-pages-diagnosis/references/diagnosis-contract.md +0 -125
  41. package/dist/skillpacks/geo/skills/geo-pages-diagnosis/references/workflow.md +0 -269
  42. package/dist/skillpacks/geo/skills/geo-writer/SKILL.md +0 -234
  43. package/dist/skillpacks/geo/skills/geo-writer/agents/openai.yaml +0 -4
  44. package/dist/skillpacks/geo/skills/geo-writer/references/content-writer-contract.md +0 -316
  45. package/dist/skillpacks/geo/skills/geo-writer/references/direct-sql.md +0 -187
  46. package/dist/skillpacks/geo/skills/geo-writer/references/seo-geo-insights.md +0 -269
  47. package/dist/skillpacks/geo/skills/seo-ecommerce-opportunity/SKILL.md +0 -82
  48. package/dist/skillpacks/geo/skills/seo-ecommerce-opportunity/agents/openai.yaml +0 -5
  49. package/dist/skillpacks/geo/skills/seo-ecommerce-opportunity/references/ecommerce-rules.md +0 -31
  50. package/dist/skillpacks/geo/skills/seo-internal-link-opportunity/SKILL.md +0 -57
  51. package/dist/skillpacks/geo/skills/seo-internal-link-opportunity/agents/openai.yaml +0 -5
  52. package/dist/skillpacks/geo/skills/seo-internal-link-opportunity/references/link-rules.md +0 -28
  53. package/dist/skillpacks/geo/skills/seo-opportunity-audit/SKILL.md +0 -141
  54. package/dist/skillpacks/geo/skills/seo-opportunity-audit/agents/openai.yaml +0 -5
  55. package/dist/skillpacks/geo/skills/seo-opportunity-audit/references/action-contract.md +0 -62
  56. package/dist/skillpacks/geo/skills/seo-opportunity-audit/scripts/seo-toolkit.mjs +0 -248
  57. package/dist/skillpacks/geo/skills/seo-page-diagnosis/SKILL.md +0 -56
  58. package/dist/skillpacks/geo/skills/seo-page-diagnosis/agents/openai.yaml +0 -5
  59. package/dist/skillpacks/geo/skills/seo-page-diagnosis/references/page-checks.md +0 -38
  60. package/dist/skillpacks/geo/skills/seo-serp-opportunity-research/SKILL.md +0 -66
  61. package/dist/skillpacks/geo/skills/seo-serp-opportunity-research/agents/openai.yaml +0 -5
  62. package/dist/skillpacks/geo/skills/seo-serp-opportunity-research/references/page-type-taxonomy.md +0 -40
  63. package/dist/skillpacks/geo/skills/seo-serp-opportunity-research/references/serp-methodology.md +0 -38
  64. package/dist/skillpacks/geo/skills/seo-technical-diagnosis/SKILL.md +0 -64
  65. package/dist/skillpacks/geo/skills/seo-technical-diagnosis/agents/openai.yaml +0 -5
  66. package/dist/skillpacks/geo/skills/seo-technical-diagnosis/references/checks.md +0 -58
  67. package/dist/skillpacks/geo/skills/third-party-diagnosis/SKILL.md +0 -54
  68. package/dist/skillpacks/geo/skills/third-party-diagnosis/agents/openai.yaml +0 -4
  69. package/dist/skillpacks/geo/skills/third-party-diagnosis/references/diagnosis-contract.md +0 -111
  70. package/dist/skillpacks/geo/skills/third-party-diagnosis/references/workflow.md +0 -174
  71. package/dist/skillpacks/geo/skills/third-party-execution-planning/SKILL.md +0 -64
  72. package/dist/skillpacks/geo/skills/third-party-execution-planning/agents/openai.yaml +0 -4
  73. package/dist/skillpacks/geo/skills/third-party-execution-planning/references/execution-contract.md +0 -90
  74. package/dist/skillpacks/geo/skills/third-party-execution-planning/references/non-social-surface-playbooks.md +0 -123
  75. package/dist/skillpacks/geo/skills/third-party-execution-planning/references/reddit-rules.md +0 -69
  76. package/dist/skillpacks/geo/skills/third-party-execution-planning/references/social-platform-playbooks.md +0 -59
@@ -1,62 +0,0 @@
1
- # SEO Action Contract
2
-
3
- Use this contract for all SEO diagnosis and opportunity outputs.
4
-
5
- ## Finding
6
-
7
- ```json
8
- {
9
- "id": "stable-local-id",
10
- "sourceSkill": "seo-technical-diagnosis",
11
- "findingType": "crawlability",
12
- "target": {
13
- "url": "https://example.com/page",
14
- "pageId": "optional",
15
- "intentId": "optional",
16
- "queryCluster": "optional"
17
- },
18
- "summary": "Concise finding",
19
- "evidence": [
20
- {
21
- "type": "crawl|html|rendered_html|dfs|cms|serp|firecrawl|product",
22
- "source": "tool, file, or URL",
23
- "claim": "What this evidence proves"
24
- }
25
- ],
26
- "confidence": "confirmed|likely|hypothesis",
27
- "severity": "critical|high|medium|low|info"
28
- }
29
- ```
30
-
31
- ## Action Candidate
32
-
33
- ```json
34
- {
35
- "sourceSkill": "seo-serp-opportunity-research",
36
- "opportunityType": "missing_page|page_type_mismatch|content_gap|technical_fix|internal_link|ecommerce_enrichment",
37
- "target": {},
38
- "recommendedAction": "Specific action",
39
- "expectedOutcome": "Observable improvement",
40
- "impact": "high|medium|low",
41
- "effort": "high|medium|low",
42
- "priority": "critical|high|medium|low",
43
- "confidence": "confirmed|likely|hypothesis",
44
- "canAutoApply": false,
45
- "requiresThirdPartyExecution": false,
46
- "verificationMethod": "How to verify after apply",
47
- "evidence": [],
48
- "actionFingerprint": "sha256..."
49
- }
50
- ```
51
-
52
- ## Rules
53
-
54
- - `confirmed`: direct evidence proves the condition.
55
- - `likely`: multiple indirect signals support it.
56
- - `hypothesis`: plausible but needs more evidence before execution.
57
- - `canAutoApply=true` only when Anymorph can perform the mutation through CMS,
58
- page generation, or managed action tooling.
59
- - Third-party tasks must route through third-party execution planning.
60
- - Duplicate actions share the same normalized target, operation, and payload
61
- fingerprint and should be merged.
62
-
@@ -1,248 +0,0 @@
1
- #!/usr/bin/env node
2
- import { createHash } from "node:crypto";
3
- import { mkdir, readFile, writeFile } from "node:fs/promises";
4
- import { dirname, join, resolve } from "node:path";
5
-
6
- const command = process.argv[2];
7
- const args = parseArgs(process.argv.slice(3));
8
-
9
- try {
10
- const result = await run(command, args);
11
- if (result !== undefined) console.log(JSON.stringify(result, null, 2));
12
- } catch (err) {
13
- const message = err instanceof Error ? err.message : String(err);
14
- console.error(message);
15
- process.exit(1);
16
- }
17
-
18
- async function run(mode, input) {
19
- if (mode === "init-artifact") return initArtifact(input);
20
- if (mode === "write-json") return writeJson(input);
21
- if (mode === "fingerprint") return fingerprint(input);
22
- if (mode === "dfs-request") return dfsRequest(input);
23
- if (mode === "dfs-call") return dfsCall(input);
24
- if (mode === "dfs-normalize") return dfsNormalize(input);
25
- if (mode === "validate-artifact") return validateArtifact(input);
26
- throw new Error("Usage: seo-toolkit.mjs <init-artifact|write-json|fingerprint|dfs-request|dfs-normalize|validate-artifact>");
27
- }
28
-
29
- function parseArgs(argv) {
30
- const out = { repo: "." };
31
- for (let i = 0; i < argv.length; i += 1) {
32
- const arg = argv[i];
33
- if (arg === "--skill") out.skill = requireValue(argv, ++i, arg);
34
- else if (arg === "--run-id") out.runId = requireValue(argv, ++i, arg);
35
- else if (arg === "--repo") out.repo = requireValue(argv, ++i, arg);
36
- else if (arg === "--name") out.name = requireValue(argv, ++i, arg);
37
- else if (arg === "--file") out.file = requireValue(argv, ++i, arg);
38
- else if (arg === "--json") out.json = requireValue(argv, ++i, arg);
39
- else if (arg === "--endpoint") out.endpoint = requireValue(argv, ++i, arg);
40
- else if (arg === "--params") out.params = requireValue(argv, ++i, arg);
41
- else if (arg === "--path") out.path = requireValue(argv, ++i, arg);
42
- else if (arg === "--payload") out.payload = requireValue(argv, ++i, arg);
43
- else throw new Error(`Unknown argument: ${arg}`);
44
- }
45
- return out;
46
- }
47
-
48
- function requireValue(argv, index, flag) {
49
- const value = argv[index];
50
- if (!value || value.startsWith("--")) throw new Error(`${flag} requires a value`);
51
- return value;
52
- }
53
-
54
- async function initArtifact(input) {
55
- const skill = required(input.skill, "--skill");
56
- const runId = required(input.runId, "--run-id");
57
- const repo = resolve(input.repo ?? ".");
58
- const dir = join(repo, "agent", "seo", skill, runId);
59
- await mkdir(dir, { recursive: true });
60
- const manifest = {
61
- skill,
62
- runId,
63
- createdAt: new Date().toISOString(),
64
- files: [],
65
- };
66
- await writeFile(join(dir, "manifest.json"), `${JSON.stringify(manifest, null, 2)}\n`);
67
- return { ok: true, dir };
68
- }
69
-
70
- async function writeJson(input) {
71
- const skill = required(input.skill, "--skill");
72
- const runId = required(input.runId, "--run-id");
73
- const name = required(input.name, "--name");
74
- const repo = resolve(input.repo ?? ".");
75
- const dir = join(repo, "agent", "seo", skill, runId);
76
- await mkdir(dir, { recursive: true });
77
- const payload = input.json ? JSON.parse(input.json) : JSON.parse(await readStdin());
78
- const path = join(dir, name.endsWith(".json") ? name : `${name}.json`);
79
- await writeFile(path, `${JSON.stringify(payload, null, 2)}\n`);
80
- return { ok: true, path };
81
- }
82
-
83
- function fingerprint(input) {
84
- const payload = input.json ? JSON.parse(input.json) : input;
85
- const canonical = stableStringify(payload);
86
- return {
87
- fingerprint: createHash("sha256").update(canonical).digest("hex"),
88
- canonical,
89
- };
90
- }
91
-
92
- function dfsRequest(input) {
93
- const endpoint = required(input.endpoint, "--endpoint");
94
- const params = input.params ? JSON.parse(input.params) : {};
95
- const cost = estimateDfsCost(endpoint, params);
96
- return {
97
- endpoint,
98
- mcpTool: endpointToMcpTool(endpoint),
99
- params,
100
- estimatedCostUsd: cost,
101
- approvalStatus: cost >= 0.25 ? "needs_approval" : "approved",
102
- notes: [
103
- "Save this request next to the skill artifact before calling DFS.",
104
- "After the MCP/API call, pipe the raw response through dfs-normalize.",
105
- ],
106
- };
107
- }
108
-
109
- async function dfsCall(input) {
110
- const apiPath = required(input.path, "--path");
111
- const payload = input.payload ? JSON.parse(input.payload) : JSON.parse(await readStdin());
112
- const login = process.env.DATAFORSEO_LOGIN || process.env.DATAFORSEO_USERNAME;
113
- const password = process.env.DATAFORSEO_PASSWORD;
114
- if (!login || !password) {
115
- throw new Error("DATAFORSEO_LOGIN/DATAFORSEO_USERNAME and DATAFORSEO_PASSWORD are required for dfs-call");
116
- }
117
- if (!apiPath.startsWith("/v3/")) throw new Error("--path must start with /v3/");
118
- const auth = Buffer.from(`${login}:${password}`).toString("base64");
119
- const response = await fetch(`https://api.dataforseo.com${apiPath}`, {
120
- method: "POST",
121
- headers: {
122
- Authorization: `Basic ${auth}`,
123
- "Content-Type": "application/json",
124
- },
125
- body: JSON.stringify(payload),
126
- });
127
- const body = await response.text();
128
- if (!response.ok) {
129
- throw new Error(`DataForSEO ${response.status}: ${body.slice(0, 1000)}`);
130
- }
131
- return JSON.parse(body);
132
- }
133
-
134
- async function dfsNormalize(input) {
135
- const endpoint = required(input.endpoint, "--endpoint");
136
- const raw = JSON.parse(await readStdin());
137
- return {
138
- endpoint,
139
- normalizedAt: new Date().toISOString(),
140
- items: extractItems(raw).map((item) => normalizeDfsItem(endpoint, item)),
141
- rawTaskStatus: extractStatus(raw),
142
- };
143
- }
144
-
145
- async function validateArtifact(input) {
146
- const file = required(input.file, "--file");
147
- const parsed = JSON.parse(await readFile(file, "utf8"));
148
- const problems = [];
149
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) problems.push("artifact must be an object");
150
- if (!parsed.runId && !parsed.skill && !parsed.actions && !parsed.findings && !parsed.opportunities) {
151
- problems.push("artifact should include runId/skill or findings/opportunities/actions");
152
- }
153
- return { ok: problems.length === 0, file, problems };
154
- }
155
-
156
- function required(value, name) {
157
- if (!value) throw new Error(`${name} is required`);
158
- return value;
159
- }
160
-
161
- async function readStdin() {
162
- const chunks = [];
163
- for await (const chunk of process.stdin) chunks.push(chunk);
164
- return Buffer.concat(chunks).toString("utf8");
165
- }
166
-
167
- function stableStringify(value) {
168
- if (Array.isArray(value)) return `[${value.map(stableStringify).join(",")}]`;
169
- if (value && typeof value === "object") {
170
- return `{${Object.keys(value)
171
- .sort()
172
- .map((key) => `${JSON.stringify(key)}:${stableStringify(value[key])}`)
173
- .join(",")}}`;
174
- }
175
- return JSON.stringify(value);
176
- }
177
-
178
- function estimateDfsCost(endpoint, params) {
179
- const count =
180
- Number(params.count) ||
181
- (params.keywords && Array.isArray(params.keywords) ? params.keywords.length : 1) ||
182
- 1;
183
- if (endpoint.includes("serp")) return Math.ceil(count / 100) * 0.002;
184
- if (endpoint.includes("keyword") || endpoint.includes("related")) return Math.max(0.005, count * 0.0001);
185
- if (endpoint.includes("competitor") || endpoint.includes("intersection")) return 0.01;
186
- if (endpoint.includes("on_page") || endpoint.includes("lighthouse")) return 0.01;
187
- return 0.005;
188
- }
189
-
190
- function endpointToMcpTool(endpoint) {
191
- const aliases = {
192
- serp_organic_live_advanced: "mcp__dataforseo__serp_organic_live_advanced",
193
- dataforseo_labs_google_related_keywords: "mcp__dataforseo__dataforseo_labs_google_related_keywords",
194
- dataforseo_labs_google_keyword_overview: "mcp__dataforseo__dataforseo_labs_google_keyword_overview",
195
- dataforseo_labs_google_competitors_domain: "mcp__dataforseo__dataforseo_labs_google_competitors_domain",
196
- dataforseo_labs_google_domain_intersection: "mcp__dataforseo__dataforseo_labs_google_domain_intersection",
197
- dataforseo_labs_google_page_intersection: "mcp__dataforseo__dataforseo_labs_google_page_intersection",
198
- dataforseo_labs_google_serp_competitors: "mcp__dataforseo__dataforseo_labs_google_serp_competitors",
199
- anymorph_dataforseo_research: "mcp__anymorph__dataforseo_research",
200
- anymorph_search_keywords: "mcp__anymorph__search_keywords",
201
- anymorph_serp_snapshot: "mcp__anymorph__serp_snapshot",
202
- anymorph_list_seo_opportunities: "mcp__anymorph__list_seo_opportunities",
203
- };
204
- return aliases[endpoint] ?? `mcp__dataforseo__${endpoint}`;
205
- }
206
-
207
- function extractItems(raw) {
208
- if (Array.isArray(raw)) return raw;
209
- if (Array.isArray(raw.items)) return raw.items;
210
- if (Array.isArray(raw.result)) return raw.result.flatMap((r) => r.items ?? r.result ?? r);
211
- if (Array.isArray(raw.tasks)) return raw.tasks.flatMap((task) => task.result ?? task.items ?? []);
212
- if (raw.data && Array.isArray(raw.data.items)) return raw.data.items;
213
- return [raw];
214
- }
215
-
216
- function extractStatus(raw) {
217
- if (raw.status_message || raw.status_code) {
218
- return { statusCode: raw.status_code, statusMessage: raw.status_message };
219
- }
220
- if (Array.isArray(raw.tasks)) {
221
- return raw.tasks.map((task) => ({
222
- statusCode: task.status_code,
223
- statusMessage: task.status_message,
224
- }));
225
- }
226
- return null;
227
- }
228
-
229
- function normalizeDfsItem(endpoint, item) {
230
- const keyword = item.keyword ?? item.keyword_data?.keyword ?? item.keyword_info?.keyword ?? item.query;
231
- const keywordInfo = item.keyword_info ?? item.keyword_data?.keyword_info ?? {};
232
- const serp = item.serp_item ?? item.first_domain_serp_element ?? item.second_domain_serp_element ?? item;
233
- return {
234
- endpoint,
235
- keyword,
236
- searchVolume: keywordInfo.search_volume ?? item.search_volume ?? null,
237
- cpc: keywordInfo.cpc ?? item.cpc ?? null,
238
- competition: keywordInfo.competition ?? keywordInfo.competition_level ?? item.competition ?? null,
239
- intent: item.search_intent_info?.main_intent ?? item.intent ?? null,
240
- position: serp.rank_absolute ?? serp.position ?? serp.avg_position ?? item.avg_position ?? null,
241
- url: serp.url ?? item.url ?? null,
242
- domain: serp.domain ?? item.domain ?? null,
243
- title: serp.title ?? item.title ?? null,
244
- description: serp.description ?? item.description ?? null,
245
- serpFeatures: item.serp_features ?? serp.serp_features ?? [],
246
- raw: item,
247
- };
248
- }
@@ -1,56 +0,0 @@
1
- ---
2
- name: seo-page-diagnosis
3
- description: Use when diagnosing one or more pages for title, meta description, headings, content depth, page intent fit, schema, images, internal links, CTAs, or page-level SEO action candidates.
4
- ---
5
-
6
- # SEO Page Diagnosis
7
-
8
- Use this skill for page-level SEO diagnosis. It is not a site infrastructure
9
- audit. It inspects the page as a search result candidate and conversion surface.
10
-
11
- Follow the `claude-seo` single-page pattern: on-page SEO, content quality,
12
- technical page elements, schema, images, and practical recommendations.
13
-
14
- ## Artifact
15
-
16
- Write outputs under:
17
-
18
- ```text
19
- agent/seo/seo-page-diagnosis/{runId}/
20
- ```
21
-
22
- Initialize with the shared toolkit from `$seo-opportunity-audit`.
23
-
24
- ## References
25
-
26
- - `references/page-checks.md` for page scoring dimensions.
27
- - `../seo-opportunity-audit/references/action-contract.md` for output shape.
28
-
29
- ## Workflow
30
-
31
- 1. Select target pages from run context, candidate URLs, weak intents, or SERP
32
- opportunity outputs.
33
- 2. Extract title, meta description, H1, heading hierarchy, canonical, robots,
34
- schema, image alt/dimensions, internal links, external links, CTA structure,
35
- word count, and freshness signals.
36
- 3. Compare page format with the intended query or intent if provided.
37
- 4. Produce page-specific actions such as title rewrite, meta rewrite, section
38
- addition, FAQ-like answer block, proof block, image alt update, schema
39
- completion, or internal link placement.
40
- 5. Separate auto-applicable CMS edits from third-party or source-code-only fixes.
41
-
42
- ## Output Files
43
-
44
- - `pages.json`: page evidence and scorecards.
45
- - `findings.json`: findings by page.
46
- - `actions.json`: page-level action candidates.
47
- - `rationale.md`: concise explanation.
48
-
49
- ## Guardrails
50
-
51
- - Do not claim ranking opportunity without SERP/DFS evidence.
52
- - Do not recommend keyword stuffing.
53
- - Do not recommend FAQ schema for Google rich result value unless the site type
54
- qualifies; FAQ-like content can still help AI answerability.
55
- - If rendered content is unavailable, label findings as limited.
56
-
@@ -1,5 +0,0 @@
1
- interface:
2
- display_name: "SEO Page Diagnosis"
3
- short_description: "Diagnose page-level SEO and content action candidates."
4
- default_prompt: "Use $seo-page-diagnosis to diagnose title, meta, headings, content depth, schema, images, and page-level SEO opportunities."
5
-
@@ -1,38 +0,0 @@
1
- # Page Checks
2
-
3
- ## On-Page SEO
4
-
5
- - title is unique, specific, and aligned with primary intent.
6
- - meta description summarizes value and supports click intent.
7
- - exactly one clear H1 in most cases.
8
- - headings form a logical outline without skipped meaning.
9
- - URL is short, stable, and descriptive.
10
- - canonical and robots directives match the desired index state.
11
-
12
- ## Content Quality
13
-
14
- - page answers the primary question directly.
15
- - content depth matches page type.
16
- - claims include proof, examples, data, screenshots, customers, or source links.
17
- - author, brand, or product expertise is visible where relevant.
18
- - stale content has last-updated or freshness risk.
19
-
20
- ## AI Answerability
21
-
22
- - include self-contained answer blocks.
23
- - define entities clearly.
24
- - state comparisons, criteria, pros/cons, and facts in extractable language.
25
- - avoid vague marketing-only claims.
26
-
27
- ## Images and Media
28
-
29
- - important images have descriptive alt text.
30
- - large hero/product images have dimensions and modern formats where possible.
31
- - visual evidence supports the page purpose.
32
-
33
- ## Links
34
-
35
- - internal links point to next useful pages.
36
- - anchors are descriptive and natural.
37
- - external links support factual claims when needed.
38
-
@@ -1,66 +0,0 @@
1
- ---
2
- name: seo-serp-opportunity-research
3
- description: Use when researching SEO opportunities from DataForSEO SERPs, keyword gaps, SERP overlap, page-type mismatch, competitor pages, query clusters, or new page opportunities.
4
- ---
5
-
6
- # SEO SERP Opportunity Research
7
-
8
- Use this skill to find search opportunities that require live SERP or keyword
9
- evidence. It combines `claude-seo` patterns from `seo-cluster`, `seo-sxo`,
10
- `seo-dataforseo`, and competitor-page guidance.
11
-
12
- DFS is the primary data source. Firecrawl is only for reading specific pages
13
- after DFS identifies competitors or candidate URLs.
14
-
15
- ## Artifact
16
-
17
- Write outputs under:
18
-
19
- ```text
20
- agent/seo/seo-serp-opportunity-research/{runId}/
21
- ```
22
-
23
- Use the shared toolkit for DFS request artifacts and normalization.
24
-
25
- ## References
26
-
27
- - `references/serp-methodology.md` for DFS/SERP workflow.
28
- - `references/page-type-taxonomy.md` for SXO classification.
29
- - `../seo-opportunity-audit/references/action-contract.md` for output shape.
30
-
31
- ## Workflow
32
-
33
- 1. Gather seed terms from intents, workspace category, product catalog, existing
34
- pages, GSC/SEO opportunity MCP results, and user input.
35
- 2. Use the SEO toolkit to create DFS request artifacts for related keywords,
36
- keyword overview, SERP competitors, ranked/intersection/page intersection, or
37
- organic SERP snapshots.
38
- 3. Normalize DFS responses before analysis.
39
- 4. Cluster queries by SERP overlap and intent.
40
- 5. Classify the dominant page type for each cluster.
41
- 6. Detect opportunities:
42
- - missing page.
43
- - page-type mismatch.
44
- - content gap.
45
- - comparison/alternatives/best-of opportunity.
46
- - weak existing page needing refresh.
47
- - internal-link support opportunity.
48
- 7. Use Firecrawl to inspect only the top competitor pages needed for evidence.
49
- 8. Emit opportunities and action candidates.
50
-
51
- ## Output Files
52
-
53
- - `dfs-requests.json`: planned DFS calls and cost tiers.
54
- - `dfs-normalized.json`: normalized DFS evidence.
55
- - `clusters.json`: query clusters and page-type consensus.
56
- - `opportunities.json`: ranked opportunities.
57
- - `actions.json`: action candidates.
58
- - `rationale.md`: concise decision record.
59
-
60
- ## Confidence Rules
61
-
62
- - `confirmed`: DFS SERP/keyword evidence and page inventory agree.
63
- - `likely`: DFS evidence is strong but page inventory or competitor scrape is
64
- incomplete.
65
- - `hypothesis`: keyword or SERP signal exists but needs validation before action.
66
-
@@ -1,5 +0,0 @@
1
- interface:
2
- display_name: "SEO SERP Opportunity Research"
3
- short_description: "Find DFS-backed keyword, SERP, and page-type opportunities."
4
- default_prompt: "Use $seo-serp-opportunity-research to find DFS-backed SERP, keyword, cluster, and competitor page opportunities."
5
-
@@ -1,40 +0,0 @@
1
- # Page-Type Taxonomy
2
-
3
- Use this for SXO-style page-type mismatch detection.
4
-
5
- ## Common Types
6
-
7
- - `homepage`
8
- - `product`
9
- - `pricing`
10
- - `feature`
11
- - `integration`
12
- - `comparison`
13
- - `alternatives`
14
- - `best_of`
15
- - `category`
16
- - `collection`
17
- - `product_detail`
18
- - `guide`
19
- - `how_to`
20
- - `glossary`
21
- - `case_study`
22
- - `review`
23
- - `tool`
24
- - `calculator`
25
- - `directory`
26
- - `local_service`
27
-
28
- ## Mismatch Examples
29
-
30
- - SERP shows product/category pages, target is a blog post: high mismatch.
31
- - SERP shows comparison/listicle pages, target is a product page: high mismatch.
32
- - SERP shows local pack/service pages, target lacks local signals: medium/high.
33
- - SERP is fragmented: opportunity for differentiation, not a hard failure.
34
-
35
- ## Consensus
36
-
37
- - Strong consensus: one type is above 60% of top organic results.
38
- - Mixed consensus: top type is 40-60%.
39
- - Fragmented: no type reaches 40%.
40
-
@@ -1,38 +0,0 @@
1
- # SERP Opportunity Methodology
2
-
3
- ## DFS-First Data Flow
4
-
5
- 1. Expand seeds with related keywords and keyword overview.
6
- 2. Fetch SERP competitors or organic SERP snapshots for candidate clusters.
7
- 3. Use domain/page intersection to find competitor keywords the workspace lacks.
8
- 4. Normalize every DFS response before reasoning.
9
- 5. Scrape competitor pages only after a cluster is worth inspecting.
10
-
11
- ## SERP Overlap
12
-
13
- Group keywords by shared top results:
14
-
15
- - 7-10 shared URLs: same target page.
16
- - 4-6 shared URLs: same cluster.
17
- - 2-3 shared URLs: adjacent clusters with cross-links.
18
- - 0-1 shared URLs: separate opportunity.
19
-
20
- Use DFS SERP data when available. If DFS is unavailable, mark results as
21
- `hypothesis` and explain the limitation.
22
-
23
- ## Opportunity Types
24
-
25
- - `missing_page`: no matching owned/GEO page exists for a valuable cluster.
26
- - `page_type_mismatch`: existing page type differs from SERP consensus.
27
- - `content_gap`: page exists but lacks expected sections/proof/schema/media.
28
- - `comparison_page_gap`: SERP expects vs/alternatives/best-of content.
29
- - `refresh_existing_page`: page exists but freshness/depth/intent is weak.
30
- - `internal_link_support`: page exists but needs stronger internal links.
31
-
32
- ## Competitor Page Rules
33
-
34
- - Use public, verifiable competitor claims only.
35
- - Separate fact tables from interpretation.
36
- - Avoid unsupported negative competitor claims.
37
- - Include last-updated recommendation for comparison pages.
38
-
@@ -1,64 +0,0 @@
1
- ---
2
- name: seo-technical-diagnosis
3
- description: Use when diagnosing technical SEO blockers, crawlability, indexability, robots, sitemap, canonicals, rendering, Core Web Vitals, security headers, AI crawler access, or technical action candidates.
4
- ---
5
-
6
- # SEO Technical Diagnosis
7
-
8
- Use this skill for site and URL-level technical SEO evidence.
9
-
10
- This follows the `claude-seo` technical audit pattern: crawlability,
11
- indexability, security, URL structure, mobile, CWV, structured data, JavaScript
12
- rendering, IndexNow, and AI crawler management.
13
-
14
- ## Artifact
15
-
16
- Write outputs under:
17
-
18
- ```text
19
- agent/seo/seo-technical-diagnosis/{runId}/
20
- ```
21
-
22
- Initialize with:
23
-
24
- ```bash
25
- SEO_TOOLKIT=.agents/skills/seo-opportunity-audit/scripts/seo-toolkit.mjs
26
- test -f "$SEO_TOOLKIT" || SEO_TOOLKIT=.claude/skills/seo-opportunity-audit/scripts/seo-toolkit.mjs
27
- node "$SEO_TOOLKIT" init-artifact --skill seo-technical-diagnosis --run-id "$RUN_ID"
28
- ```
29
-
30
- ## References
31
-
32
- Read when needed:
33
-
34
- - `references/checks.md` for technical check categories and severity rules.
35
- - `../seo-opportunity-audit/references/action-contract.md` for output shape.
36
-
37
- ## Workflow
38
-
39
- 1. Start from known workspace URLs, sitemap URLs, crawl logs, and CMS inventory.
40
- 2. Check robots, sitemap, status codes, redirects, canonical, robots meta,
41
- hreflang, title/meta availability, schema presence, JS rendering risk, mobile,
42
- CWV/PSI/DFS on-page data if available, and AI crawler access.
43
- 3. Use DFS on-page/Lighthouse only through the SEO toolkit request/normalize flow.
44
- 4. Classify each issue by indexing risk, ranking risk, AI visibility risk, and
45
- ability to auto-apply.
46
- 5. Produce `findings.json`, `actions.json`, `blocked.json`, and `rationale.md`.
47
-
48
- ## Output Rules
49
-
50
- - `critical`: blocks indexing/crawling, severe canonical/noindex conflict, 5xx on
51
- key pages, or AI/search crawler fully blocked when visibility is intended.
52
- - `high`: major templates missing canonical/schema, important pages JS-only, bad
53
- redirect chains, poor CWV with field/lab evidence.
54
- - `medium`: optimization opportunity, partial crawler issue, incomplete sitemap,
55
- missing schema on eligible pages.
56
- - `low`: polish or monitoring.
57
-
58
- Every action must include verification:
59
-
60
- - recrawl URL.
61
- - compare rendered and raw HTML.
62
- - validate schema.
63
- - rerun DFS/PSI/CWV check.
64
- - inspect CMS/source diff when auto-applied.
@@ -1,5 +0,0 @@
1
- interface:
2
- display_name: "SEO Technical Diagnosis"
3
- short_description: "Diagnose technical SEO blockers and action candidates."
4
- default_prompt: "Use $seo-technical-diagnosis to diagnose crawlability, indexability, rendering, schema, and technical SEO issues."
5
-