optbrew 0.0.0-stage → 0.1.1

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 (172) hide show
  1. package/.claude-plugin/marketplace.json +6 -0
  2. package/.claude-plugin/plugin.json +6 -0
  3. package/.codex-plugin/plugin.json +13 -0
  4. package/CHANGELOG.md +20 -0
  5. package/LICENSE +21 -0
  6. package/README.ko.md +73 -0
  7. package/README.md +72 -2
  8. package/THIRD_PARTY_NOTICES.md +10 -0
  9. package/bin/optbrew.mjs +4 -0
  10. package/docs/platforms.md +31 -0
  11. package/docs/release.md +14 -0
  12. package/lib/cli.mjs +103 -0
  13. package/lib/doctor.mjs +44 -0
  14. package/lib/install.mjs +158 -0
  15. package/lib/paths.mjs +30 -0
  16. package/lib/records.mjs +44 -0
  17. package/lib/transaction.mjs +205 -0
  18. package/lib/update.mjs +181 -0
  19. package/package.json +39 -4
  20. package/plugin.json +13 -0
  21. package/skills/optbrew/LICENSE +21 -0
  22. package/skills/optbrew/SKILL.md +17 -0
  23. package/skills/optbrew/THIRD_PARTY_NOTICES.md +10 -0
  24. package/skills/optbrew/agents/openai.yaml +4 -0
  25. package/skills/optbrew/bundle-manifest.json +663 -0
  26. package/skills/optbrew/licenses/firecrawl-seo-audit.txt +15 -0
  27. package/skills/optbrew/licenses/magnus-seo.txt +21 -0
  28. package/skills/optbrew/licenses/marketing-search.txt +21 -0
  29. package/skills/optbrew/licenses/mblode-seo.txt +21 -0
  30. package/skills/optbrew/licenses/naver-searchadvisor.txt +29 -0
  31. package/skills/optbrew/licenses/seranking-seo.txt +21 -0
  32. package/skills/optbrew/modules/firecrawl-seo-audit/GUIDE.md +64 -0
  33. package/skills/optbrew/modules/magnus-seo/GUIDE.md +79 -0
  34. package/skills/optbrew/modules/magnus-seo/README.md +45 -0
  35. package/skills/optbrew/modules/magnus-seo/assets/audit-report-template.md +76 -0
  36. package/skills/optbrew/modules/magnus-seo/references/aeo-methodology.md +105 -0
  37. package/skills/optbrew/modules/magnus-seo/references/agent-readable-content.md +28 -0
  38. package/skills/optbrew/modules/magnus-seo/references/content-and-entity-architecture.md +100 -0
  39. package/skills/optbrew/modules/magnus-seo/references/content-strategy-seo.md +142 -0
  40. package/skills/optbrew/modules/magnus-seo/references/discovery-and-freshness.md +47 -0
  41. package/skills/optbrew/modules/magnus-seo/references/evidence-boundaries.md +75 -0
  42. package/skills/optbrew/modules/magnus-seo/references/ghost-metadata.md +204 -0
  43. package/skills/optbrew/modules/magnus-seo/references/implementation-playbook.md +114 -0
  44. package/skills/optbrew/modules/magnus-seo/references/measurement-and-experimentation.md +47 -0
  45. package/skills/optbrew/modules/magnus-seo/references/onpage-seo.md +154 -0
  46. package/skills/optbrew/modules/magnus-seo/references/platform-guidance.md +30 -0
  47. package/skills/optbrew/modules/magnus-seo/references/schema-markup.md +244 -0
  48. package/skills/optbrew/modules/magnus-seo/references/source-index.md +36 -0
  49. package/skills/optbrew/modules/magnus-seo/references/structured-data.md +56 -0
  50. package/skills/optbrew/modules/magnus-seo/references/technical-seo.md +99 -0
  51. package/skills/optbrew/modules/magnus-seo/scripts/aeo_audit.py +158 -0
  52. package/skills/optbrew/modules/magnus-seo/scripts/build_prompt_matrix.py +52 -0
  53. package/skills/optbrew/modules/magnus-seo/templates/aeo-implementation-plan.md +53 -0
  54. package/skills/optbrew/modules/magnus-seo/templates/citation-observation-log.md +16 -0
  55. package/skills/optbrew/modules/magnus-seo/templates/llms.txt.template +15 -0
  56. package/skills/optbrew/modules/magnus-seo/templates/question-cluster.md +27 -0
  57. package/skills/optbrew/modules/magnus-seo/templates/robots-ai-crawlers.txt +23 -0
  58. package/skills/optbrew/modules/marketing-search/skills/ai-seo/GUIDE.md +483 -0
  59. package/skills/optbrew/modules/marketing-search/skills/ai-seo/references/agent-readiness.md +57 -0
  60. package/skills/optbrew/modules/marketing-search/skills/ai-seo/references/citations-vs-recommendations.md +85 -0
  61. package/skills/optbrew/modules/marketing-search/skills/ai-seo/references/content-patterns.md +287 -0
  62. package/skills/optbrew/modules/marketing-search/skills/ai-seo/references/content-types.md +71 -0
  63. package/skills/optbrew/modules/marketing-search/skills/ai-seo/references/format-volatility.md +98 -0
  64. package/skills/optbrew/modules/marketing-search/skills/ai-seo/references/okf.md +104 -0
  65. package/skills/optbrew/modules/marketing-search/skills/ai-seo/references/platform-ranking-factors.md +154 -0
  66. package/skills/optbrew/modules/marketing-search/skills/ai-seo/references/youtube-ai-citations.md +60 -0
  67. package/skills/optbrew/modules/marketing-search/skills/analytics/GUIDE.md +303 -0
  68. package/skills/optbrew/modules/marketing-search/skills/analytics/references/event-library.md +260 -0
  69. package/skills/optbrew/modules/marketing-search/skills/analytics/references/ga4-implementation.md +300 -0
  70. package/skills/optbrew/modules/marketing-search/skills/analytics/references/gtm-implementation.md +390 -0
  71. package/skills/optbrew/modules/marketing-search/skills/content-strategy/GUIDE.md +432 -0
  72. package/skills/optbrew/modules/marketing-search/skills/content-strategy/references/content-distribution.md +83 -0
  73. package/skills/optbrew/modules/marketing-search/skills/content-strategy/references/headless-cms.md +194 -0
  74. package/skills/optbrew/modules/marketing-search/skills/programmatic-seo/GUIDE.md +231 -0
  75. package/skills/optbrew/modules/marketing-search/skills/programmatic-seo/references/playbooks.md +308 -0
  76. package/skills/optbrew/modules/marketing-search/skills/schema/GUIDE.md +172 -0
  77. package/skills/optbrew/modules/marketing-search/skills/schema/references/schema-examples.md +398 -0
  78. package/skills/optbrew/modules/marketing-search/skills/seo-audit/GUIDE.md +492 -0
  79. package/skills/optbrew/modules/marketing-search/skills/seo-audit/references/ai-writing-detection.md +200 -0
  80. package/skills/optbrew/modules/marketing-search/skills/seo-audit/references/international-seo.md +230 -0
  81. package/skills/optbrew/modules/marketing-search/skills/site-architecture/GUIDE.md +350 -0
  82. package/skills/optbrew/modules/marketing-search/skills/site-architecture/references/mermaid-templates.md +216 -0
  83. package/skills/optbrew/modules/marketing-search/skills/site-architecture/references/navigation-patterns.md +305 -0
  84. package/skills/optbrew/modules/marketing-search/skills/site-architecture/references/site-type-templates.md +293 -0
  85. package/skills/optbrew/modules/mblode-seo/GUIDE.md +74 -0
  86. package/skills/optbrew/modules/mblode-seo/references/answer-engines.md +58 -0
  87. package/skills/optbrew/modules/mblode-seo/references/audit.md +78 -0
  88. package/skills/optbrew/modules/mblode-seo/references/brief-template.md +47 -0
  89. package/skills/optbrew/modules/mblode-seo/references/monitoring.md +56 -0
  90. package/skills/optbrew/modules/mblode-seo/references/nextjs-implementation.md +65 -0
  91. package/skills/optbrew/modules/mblode-seo/references/research-protocol.md +37 -0
  92. package/skills/optbrew/modules/mblode-seo/references/sources.md +47 -0
  93. package/skills/optbrew/modules/mblode-seo/references/validation-evidence.md +21 -0
  94. package/skills/optbrew/modules/naver-searchadvisor/GUIDE.md +75 -0
  95. package/skills/optbrew/modules/naver-searchadvisor/NOTICE.md +59 -0
  96. package/skills/optbrew/modules/naver-searchadvisor/references/INDEX.md +97 -0
  97. package/skills/optbrew/modules/naver-searchadvisor/scripts/audit.mjs +714 -0
  98. package/skills/optbrew/modules/naver-searchadvisor/scripts/robots.mjs +77 -0
  99. package/skills/optbrew/modules/seranking-seo/skills/seo-ai-search-share-of-voice/GUIDE.md +94 -0
  100. package/skills/optbrew/modules/seranking-seo/skills/seo-api/GUIDE.md +186 -0
  101. package/skills/optbrew/modules/seranking-seo/skills/seo-api/references/api-surface-map.md +207 -0
  102. package/skills/optbrew/modules/seranking-seo/skills/seo-api/references/auth-and-keys.md +182 -0
  103. package/skills/optbrew/modules/seranking-seo/skills/seo-api/references/integration-patterns.md +283 -0
  104. package/skills/optbrew/modules/seranking-seo/skills/seo-api/references/rate-limits-and-credits.md +235 -0
  105. package/skills/optbrew/modules/seranking-seo/skills/seo-competitor-gap-analysis/GUIDE.md +117 -0
  106. package/skills/optbrew/modules/seranking-seo/skills/seo-content-audit/GUIDE.md +155 -0
  107. package/skills/optbrew/modules/seranking-seo/skills/seo-content-audit/references/cite.md +84 -0
  108. package/skills/optbrew/modules/seranking-seo/skills/seo-content-audit/references/core-eeat.md +147 -0
  109. package/skills/optbrew/modules/seranking-seo/skills/seo-content-audit/templates/verdict.md +63 -0
  110. package/skills/optbrew/modules/seranking-seo/skills/seo-firecrawl/GUIDE.md +141 -0
  111. package/skills/optbrew/modules/seranking-seo/skills/seo-firecrawl/references/preflight.md +73 -0
  112. package/skills/optbrew/modules/seranking-seo/skills/seo-geo/GUIDE.md +159 -0
  113. package/skills/optbrew/modules/seranking-seo/skills/seo-google/GUIDE.md +393 -0
  114. package/skills/optbrew/modules/seranking-seo/skills/seo-google/assets/templates/cwv-audit-report.md +48 -0
  115. package/skills/optbrew/modules/seranking-seo/skills/seo-google/assets/templates/gsc-performance-report.md +44 -0
  116. package/skills/optbrew/modules/seranking-seo/skills/seo-google/assets/templates/indexation-status-report.md +43 -0
  117. package/skills/optbrew/modules/seranking-seo/skills/seo-google/references/auth-setup.md +160 -0
  118. package/skills/optbrew/modules/seranking-seo/skills/seo-google/references/cross-skill-integration.md +187 -0
  119. package/skills/optbrew/modules/seranking-seo/skills/seo-google/references/ga4-data-api.md +184 -0
  120. package/skills/optbrew/modules/seranking-seo/skills/seo-google/references/indexing-api.md +109 -0
  121. package/skills/optbrew/modules/seranking-seo/skills/seo-google/references/keyword-planner-api.md +66 -0
  122. package/skills/optbrew/modules/seranking-seo/skills/seo-google/references/nlp-api.md +55 -0
  123. package/skills/optbrew/modules/seranking-seo/skills/seo-google/references/pagespeed-crux-api.md +204 -0
  124. package/skills/optbrew/modules/seranking-seo/skills/seo-google/references/rate-limits-quotas.md +75 -0
  125. package/skills/optbrew/modules/seranking-seo/skills/seo-google/references/search-console-api.md +156 -0
  126. package/skills/optbrew/modules/seranking-seo/skills/seo-google/references/supplementary-apis.md +99 -0
  127. package/skills/optbrew/modules/seranking-seo/skills/seo-google/references/youtube-api.md +49 -0
  128. package/skills/optbrew/modules/seranking-seo/skills/seo-keyword-niche/GUIDE.md +174 -0
  129. package/skills/optbrew/modules/seranking-seo/skills/seo-page/GUIDE.md +174 -0
  130. package/skills/optbrew/modules/seranking-seo/skills/seo-plan/GUIDE.md +220 -0
  131. package/skills/optbrew/modules/seranking-seo/skills/seo-schema/GUIDE.md +135 -0
  132. package/skills/optbrew/modules/seranking-seo/skills/seo-schema/references/google-rich-results.md +89 -0
  133. package/skills/optbrew/modules/seranking-seo/skills/seo-schema/templates/article.json +23 -0
  134. package/skills/optbrew/modules/seranking-seo/skills/seo-schema/templates/breadcrumb-list.json +18 -0
  135. package/skills/optbrew/modules/seranking-seo/skills/seo-schema/templates/faq-page.json +22 -0
  136. package/skills/optbrew/modules/seranking-seo/skills/seo-schema/templates/local-business.json +32 -0
  137. package/skills/optbrew/modules/seranking-seo/skills/seo-schema/templates/product.json +25 -0
  138. package/skills/optbrew/modules/seranking-seo/skills/seo-sitemap/GUIDE.md +128 -0
  139. package/skills/optbrew/modules/seranking-seo/skills/seo-technical-audit/GUIDE.md +235 -0
  140. package/skills/optbrew/modules/seranking-seo/skills/seo-technical-audit/references/severity-mapping.md +133 -0
  141. package/skills/optbrew/references/api.md +45 -0
  142. package/skills/optbrew/references/check-registry.json +28 -0
  143. package/skills/optbrew/references/evidence.md +9 -0
  144. package/skills/optbrew/references/intake.md +9 -0
  145. package/skills/optbrew/references/measurement.md +29 -0
  146. package/skills/optbrew/references/policies.md +7 -0
  147. package/skills/optbrew/references/profiles.json +13 -0
  148. package/skills/optbrew/references/remediations.json +12 -0
  149. package/skills/optbrew/references/report.md +25 -0
  150. package/skills/optbrew/references/routing.md +15 -0
  151. package/skills/optbrew/references/schemas/contract.schema.json +51 -0
  152. package/skills/optbrew/references/scoring.md +62 -0
  153. package/skills/optbrew/scripts/adapters/anthropic.mjs +116 -0
  154. package/skills/optbrew/scripts/adapters/imports.mjs +81 -0
  155. package/skills/optbrew/scripts/audit.mjs +145 -0
  156. package/skills/optbrew/scripts/cli.mjs +41 -0
  157. package/skills/optbrew/scripts/core/bootstrap.mjs +31 -0
  158. package/skills/optbrew/scripts/core/bundle.mjs +73 -0
  159. package/skills/optbrew/scripts/core/capabilities.mjs +19 -0
  160. package/skills/optbrew/scripts/core/collector.mjs +236 -0
  161. package/skills/optbrew/scripts/core/compare.mjs +47 -0
  162. package/skills/optbrew/scripts/core/contracts.mjs +144 -0
  163. package/skills/optbrew/scripts/core/cost-ledger.mjs +95 -0
  164. package/skills/optbrew/scripts/core/findings.mjs +54 -0
  165. package/skills/optbrew/scripts/core/metrics.mjs +97 -0
  166. package/skills/optbrew/scripts/core/protocol.mjs +125 -0
  167. package/skills/optbrew/scripts/core/report.mjs +652 -0
  168. package/skills/optbrew/scripts/core/scope.mjs +52 -0
  169. package/skills/optbrew/scripts/core/score.mjs +71 -0
  170. package/skills/optbrew/scripts/core/weights.mjs +139 -0
  171. package/skills/optbrew/source-lock.json +2686 -0
  172. package/source-lock.json +2686 -0
@@ -0,0 +1,158 @@
1
+ #!/usr/bin/env python3
2
+ """Read-only structural AEO audit for local HTML or an HTTP(S) URL."""
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import json
7
+ import re
8
+ import sys
9
+ from html.parser import HTMLParser
10
+ from pathlib import Path
11
+ from urllib.parse import urljoin
12
+
13
+
14
+ class PageParser(HTMLParser):
15
+ def __init__(self) -> None:
16
+ super().__init__(convert_charrefs=True)
17
+ self.title = ""
18
+ self.headings = []
19
+ self.links = []
20
+ self.jsonld = []
21
+ self.visible = []
22
+ self._stack = []
23
+ self._heading = None
24
+ self._title_buf = None
25
+ self._script_buf = None
26
+ self.meta = []
27
+ self.canonical = None
28
+
29
+ def handle_starttag(self, tag, attrs):
30
+ attrs = dict(attrs)
31
+ if tag not in {"meta", "link", "br", "img", "input", "hr"}:
32
+ self._stack.append(tag)
33
+ if tag in {"h1", "h2", "h3", "h4", "h5", "h6"}:
34
+ heading = {"level": int(tag[1]), "text": "", "question": False}
35
+ self.headings.append(heading)
36
+ self._heading = (tag, heading, [])
37
+ elif tag == "title":
38
+ self._title_buf = []
39
+ elif tag == "a" and attrs.get("href"):
40
+ self.links.append(attrs["href"])
41
+ elif tag == "link" and (attrs.get("rel") or "").lower() == "canonical":
42
+ self.canonical = attrs.get("href")
43
+ elif tag == "meta":
44
+ self.meta.append(attrs)
45
+ elif tag == "script" and attrs.get("type", "").lower() == "application/ld+json":
46
+ self._script_buf = []
47
+
48
+ def handle_data(self, data):
49
+ if "script" in self._stack:
50
+ if self._script_buf is not None:
51
+ self._script_buf.append(data)
52
+ elif any(tag in self._stack for tag in {"style", "noscript", "template"}):
53
+ return
54
+ elif self._title_buf is not None:
55
+ self._title_buf.append(data)
56
+ else:
57
+ if self._heading is not None:
58
+ self._heading[2].append(data)
59
+ if "head" not in self._stack:
60
+ self.visible.append(data)
61
+
62
+ def handle_endtag(self, tag):
63
+ if tag == "title" and self._title_buf is not None:
64
+ self.title = re.sub(r"\s+", " ", re.sub(r"<[^>]+>", "", "".join(self._title_buf))).strip()
65
+ self._title_buf = None
66
+ elif self._heading is not None and tag == self._heading[0]:
67
+ text = re.sub(r"\s+", " ", "".join(self._heading[2])).strip()
68
+ self._heading[1]["text"] = text
69
+ self._heading[1]["question"] = text.endswith("?")
70
+ self._heading = None
71
+ elif tag == "script" and self._script_buf is not None:
72
+ raw = "".join(self._script_buf).strip()
73
+ try:
74
+ self.jsonld.append(json.loads(raw))
75
+ except json.JSONDecodeError:
76
+ self.jsonld.append({"_parse_error": True, "raw_prefix": raw[:120]})
77
+ self._script_buf = None
78
+ if tag in self._stack:
79
+ del self._stack[len(self._stack) - 1 - self._stack[::-1].index(tag):]
80
+
81
+
82
+ def load(source: str) -> tuple[str, str]:
83
+ if source.startswith(("http://", "https://")):
84
+ raise ValueError("direct remote URL disabled; use optbrew audit with a scope context")
85
+ return Path(source).read_text(encoding="utf-8"), Path(source).resolve().as_uri()
86
+
87
+
88
+ def audit(source: str) -> dict:
89
+ html, final_url = load(source)
90
+ return audit_html(html, source, final_url)
91
+
92
+
93
+ def audit_html(html: str, source: str, final_url: str | None = None) -> dict:
94
+ final_url = final_url or source
95
+ parser = PageParser()
96
+ parser.feed(html)
97
+ visible = re.sub(r"\s+", " ", " ".join(parser.visible)).strip()
98
+ robots = next((m.get("content", "") for m in parser.meta if (m.get("name") or "").lower() == "robots"), "")
99
+ types = []
100
+ parse_errors = 0
101
+ for block in parser.jsonld:
102
+ if isinstance(block, dict) and block.get("_parse_error"):
103
+ parse_errors += 1
104
+ continue
105
+ values = block if isinstance(block, list) else [block]
106
+ for value in values:
107
+ for item in value.get("@graph", [value]) if isinstance(value, dict) else []:
108
+ if isinstance(item, dict) and item.get("@type"):
109
+ types.extend(item["@type"] if isinstance(item["@type"], list) else [item["@type"]])
110
+ return {
111
+ "source": source,
112
+ "final_url": final_url,
113
+ "title": parser.title,
114
+ "canonical": urljoin(final_url, parser.canonical) if parser.canonical else None,
115
+ "robots_meta": robots,
116
+ "headings": parser.headings,
117
+ "h1_count": sum(h["level"] == 1 for h in parser.headings),
118
+ "question_heading_count": sum(h["question"] for h in parser.headings),
119
+ "answer_signals": {"question_marks": visible.count("?"), "word_count": len(visible.split())},
120
+ "link_count": len(parser.links),
121
+ "jsonld_types": sorted(set(types)),
122
+ "jsonld_blocks": len(parser.jsonld),
123
+ "jsonld_parse_errors": parse_errors,
124
+ "findings": [
125
+ *(["missing title"] if not parser.title else []),
126
+ *(["expected one H1"] if sum(h["level"] == 1 for h in parser.headings) != 1 else []),
127
+ *(["no question-shaped headings detected"] if not any(h["question"] for h in parser.headings) else []),
128
+ *(["JSON-LD parse error"] if parse_errors else []),
129
+ ],
130
+ "limitations": ["Read-only structure check; does not prove indexing, retrieval, citation, ranking, or conversion.", "Static parsing may miss content rendered only after JavaScript."],
131
+ }
132
+
133
+
134
+ def main() -> int:
135
+ ap = argparse.ArgumentParser(description=__doc__)
136
+ ap.add_argument("source", nargs="?", help="local HTML file")
137
+ ap.add_argument("--stdin", action="store_true", help="read collector-preserved HTML from stdin")
138
+ ap.add_argument("--source-url", help="source URL for --stdin evidence")
139
+ ap.add_argument("--json", action="store_true", dest="as_json", help="emit JSON (default)")
140
+ args = ap.parse_args()
141
+ try:
142
+ if args.stdin:
143
+ if not args.source_url or args.source:
144
+ raise ValueError("--stdin requires --source-url and no positional source")
145
+ result = audit_html(sys.stdin.read(), args.source_url, args.source_url)
146
+ elif args.source:
147
+ result = audit(args.source)
148
+ else:
149
+ raise ValueError("local source or --stdin required")
150
+ print(json.dumps(result, indent=2, sort_keys=True))
151
+ except (OSError, ValueError, TimeoutError) as exc:
152
+ print(f"aeo-audit: {exc}", file=sys.stderr)
153
+ return 2
154
+ return 0
155
+
156
+
157
+ if __name__ == "__main__":
158
+ raise SystemExit(main())
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env python3
2
+ """Build a deterministic AEO prompt matrix from a JSON topic file."""
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import hashlib
7
+ import json
8
+ from pathlib import Path
9
+
10
+ TEMPLATES = {
11
+ "definition": "What is {topic}, and what problem does it solve?",
12
+ "mechanism": "How does {topic} work?",
13
+ "comparison": "How does {topic} compare with {alternative}?",
14
+ "fit": "When should someone choose {topic}, and when should they not?",
15
+ "procedure": "How do I implement or use {topic} safely?",
16
+ "troubleshooting": "What are the common failure modes of {topic}, and how are they diagnosed?",
17
+ "current": "What is the current status, version, or availability of {topic}?",
18
+ }
19
+
20
+
21
+ def build(data: dict) -> list[dict]:
22
+ rows = []
23
+ for item in data.get("topics", []):
24
+ topic = item["topic"]
25
+ alternatives = item.get("alternatives", []) or ["the main alternative"]
26
+ intents = item.get("intents", list(TEMPLATES))
27
+ for intent in intents:
28
+ text = TEMPLATES[intent].format(topic=topic, alternative=alternatives[0])
29
+ rows.append({"id": f"{len(rows)+1:03d}-{intent}", "topic": topic, "intent": intent, "prompt": text})
30
+ for question in item.get("questions", []):
31
+ rows.append({"id": f"{len(rows)+1:03d}-custom", "topic": topic, "intent": "custom", "prompt": question})
32
+ for row in rows:
33
+ row["prompt_sha256"] = hashlib.sha256(row["prompt"].encode()).hexdigest()
34
+ return rows
35
+
36
+
37
+ def main() -> int:
38
+ ap = argparse.ArgumentParser(description=__doc__)
39
+ ap.add_argument("input", type=Path, help="JSON object with a topics array")
40
+ ap.add_argument("--output", type=Path, required=True)
41
+ args = ap.parse_args()
42
+ data = json.loads(args.input.read_text(encoding="utf-8"))
43
+ if not isinstance(data, dict) or not isinstance(data.get("topics"), list):
44
+ ap.error("input must be an object containing a topics array")
45
+ rows = build(data)
46
+ args.output.write_text(json.dumps({"schema_version": 1, "prompts": rows}, indent=2) + "\n", encoding="utf-8")
47
+ print(f"wrote {len(rows)} prompts to {args.output}")
48
+ return 0
49
+
50
+
51
+ if __name__ == "__main__":
52
+ raise SystemExit(main())
@@ -0,0 +1,53 @@
1
+ # AEO Implementation Plan: [site/page]
2
+
3
+ - Owner:
4
+ - Date opened:
5
+ - Canonical URL(s):
6
+ - Audience and job:
7
+ - Target surfaces:
8
+ - Non-goals:
9
+
10
+ ## Evidence boundary
11
+
12
+ - Desired user outcome:
13
+ - Observable AEO outcome:
14
+ - What this cannot prove:
15
+ - Primary sources and access dates:
16
+
17
+ ## Hypotheses
18
+
19
+ | ID | Hypothesis | Surface | Evidence | Confidence | Falsifier |
20
+ |---|---|---|---|---|---|
21
+ | H-001 | | | | | |
22
+
23
+ ## Question and entity scope
24
+
25
+ | Question | Canonical page/section | Direct answer | Entity terms | Evidence | Freshness owner |
26
+ |---|---|---|---|---|---|
27
+ | | | | | | |
28
+
29
+ ## Changes and verification
30
+
31
+ - [ ] Answer and qualifications are visible near the heading.
32
+ - [ ] Claims link to appropriate evidence.
33
+ - [ ] Structured data matches visible content.
34
+ - [ ] Robots, canonical, sitemap, and true dates are correct.
35
+ - [ ] Optional agent-readable files have an owner and documented provider scope.
36
+ - [ ] Frozen prompt set and baseline observations are stored.
37
+
38
+ | Check | Tool | Result | Evidence URL/path | Date |
39
+ |---|---|---|---|---|
40
+ | Rendered page | | | | |
41
+ | JSON-LD | | | | |
42
+ | Discovery | | | | |
43
+ | Prompt baseline/post | | | | |
44
+
45
+ ## Decision
46
+
47
+ - Status: proposed / implemented / verified / blocked
48
+ - Implemented:
49
+ - Verified:
50
+ - Observed:
51
+ - Inferred:
52
+ - Unresolved:
53
+ - Next bounded experiment or rollback:
@@ -0,0 +1,16 @@
1
+ # AEO Citation Observation Log
2
+
3
+ Use one row per provider/surface/prompt/run. Preserve the exact answer and source URLs in an attached artifact when possible.
4
+
5
+ | Run ID | Date/time UTC | Provider | Surface | Model/version | Prompt ID/hash | Answer excerpt | Cited URL(s) | Target cited? | Citation quality | Correct? | Notes |
6
+ |---|---|---|---|---|---|---|---|---|---|---|---|
7
+ | | | | | | | | | yes/no | direct / relevant / partial / irrelevant / stale / missing | yes/no/unclear | |
8
+
9
+ Definitions: **mention** is appearance without a source link; **citation** is a source link or named source; **correct** means the cited source supports the adjacent claim.
10
+
11
+ - Prompt set version:
12
+ - Retrieval/location settings:
13
+ - Account/personalization state:
14
+ - Sampling/repetition policy:
15
+ - Known provider changes:
16
+ - Interpretation limits:
@@ -0,0 +1,15 @@
1
+ # [Site or organization]
2
+
3
+ > [One concise, factual sentence describing the site and who it serves.]
4
+
5
+ [Optional context that helps an agent understand scope and terminology.]
6
+
7
+ ## Core pages
8
+
9
+ - [Page title](https://example.com/page/): [What a reader can reliably learn there.]
10
+
11
+ ## Optional
12
+
13
+ - [Full documentation](https://example.com/docs/): [Only include maintained pages.]
14
+
15
+ <!-- Follows the proposal at https://llmstxt.org/. It is optional and provider-dependent. It is not a substitute for HTML, internal links, a sitemap, or provider-native controls. -->
@@ -0,0 +1,27 @@
1
+ # AEO Question Cluster: [entity/topic]
2
+
3
+ - Cluster owner:
4
+ - Primary entity:
5
+ - Canonical page:
6
+ - Audience:
7
+ - Evidence cutoff:
8
+
9
+ | ID | User wording | Intent | Answer location | Priority | Status |
10
+ |---|---|---|---|---|---|
11
+ | Q-001 | | definition / comparison / procedure / decision / troubleshooting | | | |
12
+
13
+ ## Answer contract
14
+
15
+ - One-sentence answer:
16
+ - Important qualification:
17
+ - Entities and canonical names:
18
+ - What the answer must not imply:
19
+ - Primary evidence:
20
+ - Independent corroboration:
21
+ - Next useful link:
22
+ - Review/update trigger:
23
+
24
+ - [ ] Questions are materially distinct, not keyword permutations.
25
+ - [ ] Each question has one canonical answer location.
26
+ - [ ] The opening sentence answers the heading directly.
27
+ - [ ] Claims distinguish observation, vendor claim, inference, and uncertainty.
@@ -0,0 +1,23 @@
1
+ # AEO crawler policy decision template
2
+ # Review before turning any example into a public robots.txt.
3
+
4
+ ## Intended policy
5
+
6
+ - Search visibility allowed for:
7
+ - Training use allowed for:
8
+ - User-triggered fetch allowed for:
9
+ - Restricted paths:
10
+ - Policy owner and review date:
11
+
12
+ | Provider | Search crawler | Training crawler | User-triggered crawler | Source URL | Verified date |
13
+ |---|---|---|---|---|---|
14
+ | OpenAI | OAI-SearchBot | GPTBot | ChatGPT-User | https://platform.openai.com/docs/bots | |
15
+ | Google | Googlebot | Google-Extended | provider-specific | https://developers.google.com/search/docs/crawling-indexing/overview-google-crawlers | |
16
+ | Other | | | | | |
17
+
18
+ ## Verification
19
+
20
+ - [ ] No accidental wildcard rule changes unrelated crawlers.
21
+ - [ ] CDN/hosting layer matches the intended policy.
22
+ - [ ] Public /robots.txt matches the approved file.
23
+ - [ ] The policy does not claim to control user-triggered requests where the provider says it does not.