anymorph 0.4.0 → 0.5.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/README.md +3 -3
- package/dist/index.js +78 -6
- package/package.json +1 -1
- package/dist/skillpacks/geo/scaffold/AGENTS.md +0 -38
- package/dist/skillpacks/geo/scaffold/CLAUDE.md +0 -33
- package/dist/skillpacks/geo/shared/evidence-principles.md +0 -35
- package/dist/skillpacks/geo/shared/geo-principles.md +0 -281
- package/dist/skillpacks/geo/shared/vertical-playbooks/beauty.md +0 -65
- package/dist/skillpacks/geo/shared/vertical-playbooks/commerce.md +0 -65
- package/dist/skillpacks/geo/shared/vertical-playbooks/ota.md +0 -62
- package/dist/skillpacks/geo/shared/vertical-playbooks/saas.md +0 -64
- package/dist/skillpacks/geo/skills/brand-owned-diagnosis/SKILL.md +0 -54
- package/dist/skillpacks/geo/skills/brand-owned-diagnosis/agents/openai.yaml +0 -4
- package/dist/skillpacks/geo/skills/brand-owned-diagnosis/references/diagnosis-contract.md +0 -95
- package/dist/skillpacks/geo/skills/brand-owned-diagnosis/references/workflow.md +0 -194
- package/dist/skillpacks/geo/skills/geo-generating-actions/SKILL.md +0 -188
- package/dist/skillpacks/geo/skills/geo-generating-actions/agents/openai.yaml +0 -4
- package/dist/skillpacks/geo/skills/geo-generating-actions/references/orchestrator.workflow.md +0 -440
- package/dist/skillpacks/geo/skills/geo-generating-actions/scripts/geo-scaffold.mjs +0 -358
- package/dist/skillpacks/geo/skills/geo-generating-actions/scripts/geo.mjs +0 -66
- package/dist/skillpacks/geo/skills/geo-initializing-strategy/SKILL.md +0 -50
- package/dist/skillpacks/geo/skills/geo-initializing-strategy/agents/openai.yaml +0 -4
- package/dist/skillpacks/geo/skills/geo-initializing-strategy/references/external-authority-diagnosis.md +0 -66
- package/dist/skillpacks/geo/skills/geo-initializing-strategy/references/foundation-diagnosis.md +0 -86
- package/dist/skillpacks/geo/skills/geo-initializing-strategy/references/memory-contract.md +0 -15
- package/dist/skillpacks/geo/skills/geo-initializing-strategy/references/semantic-clusters-diagnosis.md +0 -58
- package/dist/skillpacks/geo/skills/geo-initializing-strategy/references/strategy-map-contract.md +0 -26
- package/dist/skillpacks/geo/skills/geo-initializing-strategy/references/visibility-diagnosis.md +0 -50
- package/dist/skillpacks/geo/skills/geo-local-setup/SKILL.md +0 -66
- package/dist/skillpacks/geo/skills/geo-local-setup/agents/openai.yaml +0 -4
- package/dist/skillpacks/geo/skills/geo-page-writer/SKILL.md +0 -81
- package/dist/skillpacks/geo/skills/geo-page-writer/agents/openai.yaml +0 -4
- package/dist/skillpacks/geo/skills/geo-page-writer/references/research.md +0 -61
- package/dist/skillpacks/geo/skills/geo-page-writer/references/validation.md +0 -59
- package/dist/skillpacks/geo/skills/geo-page-writer/references/writing.md +0 -55
- package/dist/skillpacks/geo/skills/geo-page-writer/scripts/check-page-mdx.mjs +0 -210
- package/dist/skillpacks/geo/skills/geo-page-writer/scripts/collect-page-sources.mjs +0 -303
- package/dist/skillpacks/geo/skills/geo-pages-diagnosis/SKILL.md +0 -51
- package/dist/skillpacks/geo/skills/geo-pages-diagnosis/agents/openai.yaml +0 -4
- package/dist/skillpacks/geo/skills/geo-pages-diagnosis/references/diagnosis-contract.md +0 -125
- package/dist/skillpacks/geo/skills/geo-pages-diagnosis/references/workflow.md +0 -269
- package/dist/skillpacks/geo/skills/geo-writer/SKILL.md +0 -234
- package/dist/skillpacks/geo/skills/geo-writer/agents/openai.yaml +0 -4
- package/dist/skillpacks/geo/skills/geo-writer/references/content-writer-contract.md +0 -316
- package/dist/skillpacks/geo/skills/geo-writer/references/direct-sql.md +0 -187
- package/dist/skillpacks/geo/skills/geo-writer/references/seo-geo-insights.md +0 -269
- package/dist/skillpacks/geo/skills/seo-ecommerce-opportunity/SKILL.md +0 -82
- package/dist/skillpacks/geo/skills/seo-ecommerce-opportunity/agents/openai.yaml +0 -5
- package/dist/skillpacks/geo/skills/seo-ecommerce-opportunity/references/ecommerce-rules.md +0 -31
- package/dist/skillpacks/geo/skills/seo-internal-link-opportunity/SKILL.md +0 -57
- package/dist/skillpacks/geo/skills/seo-internal-link-opportunity/agents/openai.yaml +0 -5
- package/dist/skillpacks/geo/skills/seo-internal-link-opportunity/references/link-rules.md +0 -28
- package/dist/skillpacks/geo/skills/seo-opportunity-audit/SKILL.md +0 -141
- package/dist/skillpacks/geo/skills/seo-opportunity-audit/agents/openai.yaml +0 -5
- package/dist/skillpacks/geo/skills/seo-opportunity-audit/references/action-contract.md +0 -62
- package/dist/skillpacks/geo/skills/seo-opportunity-audit/scripts/seo-toolkit.mjs +0 -248
- package/dist/skillpacks/geo/skills/seo-page-diagnosis/SKILL.md +0 -56
- package/dist/skillpacks/geo/skills/seo-page-diagnosis/agents/openai.yaml +0 -5
- package/dist/skillpacks/geo/skills/seo-page-diagnosis/references/page-checks.md +0 -38
- package/dist/skillpacks/geo/skills/seo-serp-opportunity-research/SKILL.md +0 -66
- package/dist/skillpacks/geo/skills/seo-serp-opportunity-research/agents/openai.yaml +0 -5
- package/dist/skillpacks/geo/skills/seo-serp-opportunity-research/references/page-type-taxonomy.md +0 -40
- package/dist/skillpacks/geo/skills/seo-serp-opportunity-research/references/serp-methodology.md +0 -38
- package/dist/skillpacks/geo/skills/seo-technical-diagnosis/SKILL.md +0 -64
- package/dist/skillpacks/geo/skills/seo-technical-diagnosis/agents/openai.yaml +0 -5
- package/dist/skillpacks/geo/skills/seo-technical-diagnosis/references/checks.md +0 -58
- package/dist/skillpacks/geo/skills/third-party-diagnosis/SKILL.md +0 -54
- package/dist/skillpacks/geo/skills/third-party-diagnosis/agents/openai.yaml +0 -4
- package/dist/skillpacks/geo/skills/third-party-diagnosis/references/diagnosis-contract.md +0 -111
- package/dist/skillpacks/geo/skills/third-party-diagnosis/references/workflow.md +0 -174
- package/dist/skillpacks/geo/skills/third-party-execution-planning/SKILL.md +0 -64
- package/dist/skillpacks/geo/skills/third-party-execution-planning/agents/openai.yaml +0 -4
- package/dist/skillpacks/geo/skills/third-party-execution-planning/references/execution-contract.md +0 -90
- package/dist/skillpacks/geo/skills/third-party-execution-planning/references/non-social-surface-playbooks.md +0 -123
- package/dist/skillpacks/geo/skills/third-party-execution-planning/references/reddit-rules.md +0 -69
- package/dist/skillpacks/geo/skills/third-party-execution-planning/references/social-platform-playbooks.md +0 -59
|
@@ -1,316 +0,0 @@
|
|
|
1
|
-
# Content Writer Contract
|
|
2
|
-
|
|
3
|
-
Read this reference when building a page-generation brief, research-only outline, or handoff context for `generate_managed_pages`.
|
|
4
|
-
|
|
5
|
-
## Input Contract
|
|
6
|
-
|
|
7
|
-
Auto mode accepts only:
|
|
8
|
-
|
|
9
|
-
```yaml
|
|
10
|
-
domain: "example.com"
|
|
11
|
-
lang: "ko"
|
|
12
|
-
topic: "free-form topic"
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
Full mode accepts:
|
|
16
|
-
|
|
17
|
-
```yaml
|
|
18
|
-
domain: "example.com"
|
|
19
|
-
workspaceId: "optional"
|
|
20
|
-
lang: "ko"
|
|
21
|
-
market: "KR"
|
|
22
|
-
topic: "free-form topic"
|
|
23
|
-
output_mode: "generation_brief"
|
|
24
|
-
data_mode: "mcp_first"
|
|
25
|
-
target_reader: "optional"
|
|
26
|
-
page_goal: "optional"
|
|
27
|
-
content_type: "optional"
|
|
28
|
-
primary_question: "optional"
|
|
29
|
-
cta_goal: "optional"
|
|
30
|
-
title_selection_mode: "optional"
|
|
31
|
-
route_or_slug: "optional"
|
|
32
|
-
pageId: "optional"
|
|
33
|
-
intentId: "optional"
|
|
34
|
-
promptIds: []
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
In Auto mode, include an `Inference Summary` before the Data Pack:
|
|
38
|
-
|
|
39
|
-
```markdown
|
|
40
|
-
## Inference Summary
|
|
41
|
-
|
|
42
|
-
- Workspace: value (source: matched from domain | user-provided | needs confirmation)
|
|
43
|
-
- Language: value (source: user-provided)
|
|
44
|
-
- Market: value (source: brand profile | language fallback | user-provided)
|
|
45
|
-
- Output mode: value (source: inferred default | user-provided)
|
|
46
|
-
- Content type: value (source: inferred from topic)
|
|
47
|
-
- Target reader: value (source: brand profile | inferred)
|
|
48
|
-
- Primary question: value (source: generated from topic)
|
|
49
|
-
- CTA goal: value (source: workspace instructions | inferred)
|
|
50
|
-
- Title selection mode: value (source: inferred default | user-provided)
|
|
51
|
-
- Route/slug: value (source: inferred; collision check: pass/fail)
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
Rules:
|
|
55
|
-
|
|
56
|
-
- Mark inferred fields explicitly.
|
|
57
|
-
- Ask the user only when inference affects workspace identity, PRD direct SQL, route collision, or publish/generation side effects.
|
|
58
|
-
- Default output mode is `generation_brief`, not live page generation.
|
|
59
|
-
- Direct SQL is never enabled by Auto mode alone.
|
|
60
|
-
- Default `title_selection_mode` is `user_select_before_generation`. Use `auto_recommended` only when the user explicitly asks to auto-pick the title.
|
|
61
|
-
|
|
62
|
-
## Data Pack Shape
|
|
63
|
-
|
|
64
|
-
```ts
|
|
65
|
-
type ContentWriterDataPack = {
|
|
66
|
-
workspace: {
|
|
67
|
-
workspaceId: string;
|
|
68
|
-
domain: string;
|
|
69
|
-
globalRules?: string;
|
|
70
|
-
knowledgeBaseFacts: Array<{ source: string; fact: string; confidence: number }>;
|
|
71
|
-
};
|
|
72
|
-
brand: {
|
|
73
|
-
name: string;
|
|
74
|
-
aliases: string[];
|
|
75
|
-
description?: string;
|
|
76
|
-
uvp?: string;
|
|
77
|
-
audience?: string;
|
|
78
|
-
positioning?: unknown;
|
|
79
|
-
voiceKit?: unknown;
|
|
80
|
-
rules: unknown[];
|
|
81
|
-
bestPractices: unknown[];
|
|
82
|
-
blockList: unknown[];
|
|
83
|
-
};
|
|
84
|
-
opportunity: {
|
|
85
|
-
primaryIntent: string;
|
|
86
|
-
primaryQuestion: string;
|
|
87
|
-
contentType?: "guide" | "comparison" | "alternatives" | "what-is" | "benchmark" | "pricing-roi" | "docs-api" | "case-study" | "glossary";
|
|
88
|
-
pageGoal?: string;
|
|
89
|
-
ctaGoal?: string;
|
|
90
|
-
titleSelectionMode?: "user_select_before_generation" | "auto_recommended" | "user_provided";
|
|
91
|
-
promptSet: Array<{ promptId?: string; template: string; tier?: string; structure?: string }>;
|
|
92
|
-
seoQueries: Array<{ query: string; volume?: number; source: "gsc" | "dfs" | "db" | "user" }>;
|
|
93
|
-
fanOutQuestions: string[];
|
|
94
|
-
};
|
|
95
|
-
existingCoverage: {
|
|
96
|
-
relatedPages: Array<{ path: string; title?: string; description?: string; intentId?: string }>;
|
|
97
|
-
cannibalizationRisks: string[];
|
|
98
|
-
internalLinkTargets: Array<{ path: string; anchorReason: string }>;
|
|
99
|
-
};
|
|
100
|
-
evidence: {
|
|
101
|
-
firstParty: Array<{ id: string; claim: string; source: string; url?: string }>;
|
|
102
|
-
thirdParty: Array<{ id: string; claim: string; sourceName: string; url: string; date?: string }>;
|
|
103
|
-
proprietary: Array<{ id: string; insight: string; method?: string; sample?: string; limitation?: string }>;
|
|
104
|
-
disallowedClaims: string[];
|
|
105
|
-
};
|
|
106
|
-
editorialKernel: EditorialKernel;
|
|
107
|
-
sectionContract: SectionEvidenceContract[];
|
|
108
|
-
};
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
## Editorial Kernel Template
|
|
112
|
-
|
|
113
|
-
```markdown
|
|
114
|
-
## Editorial Kernel
|
|
115
|
-
|
|
116
|
-
- Primary question:
|
|
117
|
-
- Reader job:
|
|
118
|
-
- Answer promise:
|
|
119
|
-
- Original angle:
|
|
120
|
-
- Evidence wedge:
|
|
121
|
-
- Honest brand role:
|
|
122
|
-
- Hook:
|
|
123
|
-
- Title posture:
|
|
124
|
-
- Caveats:
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
Rules:
|
|
128
|
-
|
|
129
|
-
- `answerPromise` must be a true direct answer, not a teaser.
|
|
130
|
-
- `originalAngle` must explain what is not commodity content.
|
|
131
|
-
- `evidenceWedge` should name the evidence type: proprietary data, product fact, benchmark, method, comparison table, first-hand workflow, or third-party source set.
|
|
132
|
-
- `honestBrandRole` must include non-fit or limitation when relevant.
|
|
133
|
-
- `titlePosture` must choose how brave the title should be: `orthodox`, `hybrid`, or `pov-led`.
|
|
134
|
-
|
|
135
|
-
## Title Strategy
|
|
136
|
-
|
|
137
|
-
Do not use one uniform title style for every page. Title posture must follow content type, reader risk, evidence strength, and funnel stage.
|
|
138
|
-
|
|
139
|
-
| Title posture | Use when | Good shape | Avoid |
|
|
140
|
-
| --- | --- | --- | --- |
|
|
141
|
-
| `orthodox` | what-is, glossary, docs/API, pricing/ROI, regulated or high-risk topics, exact search intent pages | `Topic + audience/use case + concrete scope` | cleverness that hides the answer |
|
|
142
|
-
| `hybrid` | commercial guides, service evaluation, comparison, alternatives, implementation guides | `Clear keyword + buyer tension + decision criterion` | generic "complete guide" phrasing |
|
|
143
|
-
| `pov-led` | benchmark/report, category creation, thought leadership, contrarian insight, proprietary data pages | `Specific POV or surprising finding + evidence promise` | unsupported hot takes or clickbait |
|
|
144
|
-
|
|
145
|
-
Rules:
|
|
146
|
-
|
|
147
|
-
- Separate `seoTitle` from `h1` when useful. `seoTitle` can be safer and query-clear; `h1` can carry the human hook.
|
|
148
|
-
- Keep the primary topic/entity visible in at least one title surface: `seoTitle`, `h1`, or slug.
|
|
149
|
-
- Generate at least 3 title candidates: `safe`, `hybrid`, and `bold`. Pick one `recommended` candidate and explain why in one sentence.
|
|
150
|
-
- Generate a Title Candidate Set, not a single title. Use 3 candidates for narrow/orthodox pages and 5-7 candidates for strategic commercial, comparison, alternatives, benchmark, or POV-led pages.
|
|
151
|
-
- Candidate IDs must be stable (`T1`, `T2`, etc.) so the user can choose quickly.
|
|
152
|
-
- Each candidate package should include `seoTitle`, `h1`, `socialInternalTitle`, `posture`, `bestFor`, `risk`, and a one-sentence rationale.
|
|
153
|
-
- For `managed_generation`, title selection is a gate: if `title_selection_mode` is `user_select_before_generation` and the user has not chosen a candidate, stop after the brief and ask the user to choose by ID or edit one candidate.
|
|
154
|
-
- If the user chooses or edits a title, treat that as `user_provided`; preserve it unless it violates trust, evidence, or anti-hype rules.
|
|
155
|
-
- A bolder title is allowed only when the Data Pack has a real `originalAngle` or `evidenceWedge`; otherwise prefer `orthodox` or `hybrid`.
|
|
156
|
-
- Never use unsupported superlatives such as "1위", "최고", "완벽한", "유일한", or "검증된" unless exact evidence exists.
|
|
157
|
-
- Avoid stale AI-title patterns: "궁극의 가이드", "완벽 가이드", "성공을 위한", "알아야 할 모든 것", "디지털 시대의".
|
|
158
|
-
|
|
159
|
-
Examples for a Korean B2B GEO service evaluation page:
|
|
160
|
-
|
|
161
|
-
| Candidate type | Example |
|
|
162
|
-
| --- | --- |
|
|
163
|
-
| safe `seoTitle` | `GEO 서비스 선택 가이드: 한국 B2B 마케터가 봐야 할 AI 검색 가시성 기준` |
|
|
164
|
-
| hybrid `h1` | `GEO 서비스는 블로그를 몇 개 써주느냐가 아니라, 어떤 AI 구매 질문에서 빠졌는지를 보여줘야 한다` |
|
|
165
|
-
| checklist | `GEO 서비스 평가 체크리스트: 언급률보다 먼저 봐야 할 6가지` |
|
|
166
|
-
| POC-led | `GEO 서비스 POC를 2주 안에 검증하는 법` |
|
|
167
|
-
| bold, only with evidence | `AI 답변에 우리 브랜드가 빠진다면, GEO 서비스는 여기서 갈린다` |
|
|
168
|
-
|
|
169
|
-
Output title strategy in every generation brief:
|
|
170
|
-
|
|
171
|
-
```markdown
|
|
172
|
-
## Title Strategy
|
|
173
|
-
|
|
174
|
-
- Title posture: orthodox | hybrid | pov-led
|
|
175
|
-
- Title selection mode: user_select_before_generation | auto_recommended | user_provided
|
|
176
|
-
- Reason:
|
|
177
|
-
- Recommended candidate ID:
|
|
178
|
-
|
|
179
|
-
### Title Candidate Set
|
|
180
|
-
|
|
181
|
-
| ID | Posture | SEO title | H1 | Social/internal title | Best for | Risk |
|
|
182
|
-
| --- | --- | --- | --- | --- | --- | --- |
|
|
183
|
-
| T1 | orthodox | | | | | |
|
|
184
|
-
| T2 | hybrid | | | | | |
|
|
185
|
-
| T3 | pov-led | | | | | |
|
|
186
|
-
|
|
187
|
-
Selection note: choose a candidate ID or edit one title before live generation.
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
## Section Evidence Contract
|
|
191
|
-
|
|
192
|
-
```markdown
|
|
193
|
-
## Section Evidence Contract
|
|
194
|
-
|
|
195
|
-
1. H2:
|
|
196
|
-
- Question answered:
|
|
197
|
-
- Answer capsule:
|
|
198
|
-
- Use evidence IDs:
|
|
199
|
-
- Forbidden claims:
|
|
200
|
-
- Internal link:
|
|
201
|
-
- Format/component:
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
Rules:
|
|
205
|
-
|
|
206
|
-
- Every H2 answers one concrete subquestion.
|
|
207
|
-
- The answer capsule must work as a standalone quotation.
|
|
208
|
-
- Evidence IDs must map to Data Pack evidence.
|
|
209
|
-
- Do not invent an evidence ID to justify a section.
|
|
210
|
-
- Use tables only for decision-useful comparisons.
|
|
211
|
-
|
|
212
|
-
## Managed Generation Brief Template
|
|
213
|
-
|
|
214
|
-
Use this shape in `generate_managed_pages.context`.
|
|
215
|
-
|
|
216
|
-
```markdown
|
|
217
|
-
# Page Generation Brief
|
|
218
|
-
|
|
219
|
-
## Workspace and Brand
|
|
220
|
-
- Workspace:
|
|
221
|
-
- Domain:
|
|
222
|
-
- Brand:
|
|
223
|
-
- Target language/market:
|
|
224
|
-
- Brand voice summary:
|
|
225
|
-
- Global rules:
|
|
226
|
-
|
|
227
|
-
## Page Objective
|
|
228
|
-
- Primary intent:
|
|
229
|
-
- Primary question:
|
|
230
|
-
- Reader job:
|
|
231
|
-
- Search/GEO demand:
|
|
232
|
-
- Target prompt set:
|
|
233
|
-
- Fan-out questions to cover:
|
|
234
|
-
- Fan-out questions to skip:
|
|
235
|
-
|
|
236
|
-
## Editorial Kernel
|
|
237
|
-
[paste kernel]
|
|
238
|
-
|
|
239
|
-
## Evidence Library
|
|
240
|
-
- [E1] claim, source, URL/date
|
|
241
|
-
- [E2] claim, source, URL/date
|
|
242
|
-
|
|
243
|
-
## Section Evidence Contract
|
|
244
|
-
[paste contract]
|
|
245
|
-
|
|
246
|
-
## Internal Links
|
|
247
|
-
- path: anchor reason
|
|
248
|
-
|
|
249
|
-
## Claims Policy
|
|
250
|
-
- Allowed claims:
|
|
251
|
-
- Disallowed claims:
|
|
252
|
-
- Claims requiring caveat:
|
|
253
|
-
|
|
254
|
-
## Title Strategy
|
|
255
|
-
- Title selection mode:
|
|
256
|
-
- Recommended candidate ID:
|
|
257
|
-
- Selected candidate ID or user-edited title:
|
|
258
|
-
- Selection status: needs_user_selection | selected | auto_recommended
|
|
259
|
-
|
|
260
|
-
### Title Candidate Set
|
|
261
|
-
| ID | Posture | SEO title | H1 | Social/internal title | Best for | Risk |
|
|
262
|
-
| --- | --- | --- | --- | --- | --- | --- |
|
|
263
|
-
|
|
264
|
-
## Metadata Intent
|
|
265
|
-
- Title posture:
|
|
266
|
-
- SEO title:
|
|
267
|
-
- H1:
|
|
268
|
-
- Social/internal title:
|
|
269
|
-
- Description intent:
|
|
270
|
-
- Slug:
|
|
271
|
-
- Structured data intent:
|
|
272
|
-
|
|
273
|
-
## QA Rules
|
|
274
|
-
- Answer-first intro and H2s.
|
|
275
|
-
- No invented stats, quotes, dates, testimonials, screenshots, or product claims.
|
|
276
|
-
- Use exact source URLs where statistics or studies are cited.
|
|
277
|
-
- Keep brand mention natural and useful.
|
|
278
|
-
- If live generation is requested and title selection status is `needs_user_selection`, stop and ask for a title candidate ID instead of calling generation.
|
|
279
|
-
```
|
|
280
|
-
|
|
281
|
-
## Research-Only Output Template
|
|
282
|
-
|
|
283
|
-
```markdown
|
|
284
|
-
## Content Brief
|
|
285
|
-
|
|
286
|
-
### Why this page should exist
|
|
287
|
-
|
|
288
|
-
### What question it answers
|
|
289
|
-
|
|
290
|
-
### Editorial kernel
|
|
291
|
-
|
|
292
|
-
### Evidence gaps
|
|
293
|
-
|
|
294
|
-
### Proposed outline
|
|
295
|
-
|
|
296
|
-
### Generation-ready context
|
|
297
|
-
|
|
298
|
-
### Risks and caveats
|
|
299
|
-
```
|
|
300
|
-
|
|
301
|
-
## QA Rubric
|
|
302
|
-
|
|
303
|
-
Score each 0-2. Minimum pass: 16/20 and no zero in Trust or Evidence.
|
|
304
|
-
|
|
305
|
-
| Dimension | 0 | 1 | 2 |
|
|
306
|
-
| --- | --- | --- | --- |
|
|
307
|
-
| People-first usefulness | generic | partly useful | completes reader job |
|
|
308
|
-
| E-E-A-T trust | unclear | some signals | clear who/how/why/source |
|
|
309
|
-
| Originality | commodity | slight POV | unique evidence/angle |
|
|
310
|
-
| Answer-first citability | buried | mixed | direct capsules |
|
|
311
|
-
| Evidence density | unsupported | partial | claims mapped to evidence |
|
|
312
|
-
| Fan-out coverage | misses intent | covers basics | covers key subquestions |
|
|
313
|
-
| Scannability | dense | acceptable | headings/lists/tables help |
|
|
314
|
-
| Hook/title quality | hype or generic | clear but plain | concrete, intent-clear, and compelling |
|
|
315
|
-
| Brand honesty | forced | acceptable | useful fit/non-fit |
|
|
316
|
-
| Technical readiness | unclear | partial | crawl/index/schema aware |
|
|
@@ -1,187 +0,0 @@
|
|
|
1
|
-
# Direct SQL Extraction
|
|
2
|
-
|
|
3
|
-
Use this reference only when Anymorph MCP tools cannot return the needed read-only data slice or the user explicitly asks to query the Anymorph database.
|
|
4
|
-
|
|
5
|
-
## Rule
|
|
6
|
-
|
|
7
|
-
MCP first. SQL second. Read-only always.
|
|
8
|
-
|
|
9
|
-
The backend endpoint can execute writes when the query is not a `SELECT`, so the agent must enforce a strict guard before every call.
|
|
10
|
-
|
|
11
|
-
## Endpoint
|
|
12
|
-
|
|
13
|
-
DEV:
|
|
14
|
-
|
|
15
|
-
```bash
|
|
16
|
-
doppler run -p anymorph-saul-mcp -c dev -- bash -lc '
|
|
17
|
-
curl -s -X POST "https://api-dev.anymorph.ai/api/admin/sql" \
|
|
18
|
-
-H "Authorization: Bearer $BACKEND_INTERNAL_TOKEN" \
|
|
19
|
-
-H "Content-Type: application/json" \
|
|
20
|
-
-d "{\"query\":\"SELECT 1 AS ok\"}"
|
|
21
|
-
'
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
PRD:
|
|
25
|
-
|
|
26
|
-
```bash
|
|
27
|
-
doppler run -p anymorph-saul-mcp -c prd -- bash -lc '
|
|
28
|
-
curl -s -X POST "https://api.anymorph.ai/api/admin/sql" \
|
|
29
|
-
-H "Authorization: Bearer $BACKEND_INTERNAL_TOKEN" \
|
|
30
|
-
-H "Content-Type: application/json" \
|
|
31
|
-
-d "{\"query\":\"SELECT 1 AS ok\"}"
|
|
32
|
-
'
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
If `anymorph-saul-mcp` is unavailable but backend secrets are available, the backend project pattern may expose `CRON_SECRET`; use the same endpoint with `Authorization: Bearer $CRON_SECRET`.
|
|
36
|
-
|
|
37
|
-
## Required Guard
|
|
38
|
-
|
|
39
|
-
Allow only:
|
|
40
|
-
|
|
41
|
-
- one statement
|
|
42
|
-
- starts with `SELECT` or `WITH ... SELECT`
|
|
43
|
-
- scoped by `workspaceId` unless querying `information_schema`
|
|
44
|
-
- has `LIMIT` for row-producing queries
|
|
45
|
-
- default max rows 20, hard cap 100
|
|
46
|
-
|
|
47
|
-
Reject if query contains any of:
|
|
48
|
-
|
|
49
|
-
```text
|
|
50
|
-
INSERT UPDATE DELETE DROP ALTER TRUNCATE CREATE REPLACE MERGE
|
|
51
|
-
GRANT REVOKE COPY VACUUM ANALYZE CALL DO EXECUTE
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
Reject semicolons unless the semicolon is the final character and there is no second statement.
|
|
55
|
-
|
|
56
|
-
## Schema Smoke Query
|
|
57
|
-
|
|
58
|
-
```sql
|
|
59
|
-
SELECT table_name
|
|
60
|
-
FROM information_schema.tables
|
|
61
|
-
WHERE table_schema = 'public'
|
|
62
|
-
AND table_name IN (
|
|
63
|
-
'Workspace',
|
|
64
|
-
'Brand',
|
|
65
|
-
'Page',
|
|
66
|
-
'Intent',
|
|
67
|
-
'TrackedPrompt',
|
|
68
|
-
'AnalysisItemResult',
|
|
69
|
-
'ResponseInsightV2',
|
|
70
|
-
'SearchQueryCluster',
|
|
71
|
-
'PagePrompt'
|
|
72
|
-
)
|
|
73
|
-
ORDER BY table_name
|
|
74
|
-
LIMIT 20;
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
## Workspace and Brand Context
|
|
78
|
-
|
|
79
|
-
```sql
|
|
80
|
-
SELECT
|
|
81
|
-
w.id,
|
|
82
|
-
w.domain,
|
|
83
|
-
w."globalRules",
|
|
84
|
-
left(w."knowledgeBaseContent", 4000) AS knowledge_base_excerpt,
|
|
85
|
-
b.name AS brand_name,
|
|
86
|
-
b.description,
|
|
87
|
-
b."uniqueValueProposition",
|
|
88
|
-
b."targetAudiencePrimary",
|
|
89
|
-
b."voiceAxes",
|
|
90
|
-
b.rules,
|
|
91
|
-
b."bestPractices",
|
|
92
|
-
b."blockList",
|
|
93
|
-
b."seoKeywordsData"
|
|
94
|
-
FROM "Workspace" w
|
|
95
|
-
LEFT JOIN "Brand" b ON b."workspaceId" = w.id
|
|
96
|
-
WHERE w.id = '<workspaceId>'
|
|
97
|
-
LIMIT 1;
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
## Intent and Prompt Opportunity
|
|
101
|
-
|
|
102
|
-
```sql
|
|
103
|
-
SELECT
|
|
104
|
-
i.id AS intent_id,
|
|
105
|
-
i.name AS intent_name,
|
|
106
|
-
i.description,
|
|
107
|
-
i.type,
|
|
108
|
-
i."combinedVolume",
|
|
109
|
-
i."currentVisibility",
|
|
110
|
-
i."competitorVisibility",
|
|
111
|
-
p.id AS prompt_id,
|
|
112
|
-
p.template,
|
|
113
|
-
p.intent,
|
|
114
|
-
p."funnelStage",
|
|
115
|
-
p."estimatedVolume",
|
|
116
|
-
p."businessValue",
|
|
117
|
-
p.structure,
|
|
118
|
-
p."promptCategory",
|
|
119
|
-
p."promptTier",
|
|
120
|
-
p."snapshotVisibilityRate",
|
|
121
|
-
p."snapshotHasMention"
|
|
122
|
-
FROM "Intent" i
|
|
123
|
-
LEFT JOIN "TrackedPrompt" p ON p."intentId" = i.id
|
|
124
|
-
WHERE i."workspaceId" = '<workspaceId>'
|
|
125
|
-
AND i."deletedAt" IS NULL
|
|
126
|
-
ORDER BY i."currentVisibility" ASC NULLS LAST, i."combinedVolume" DESC NULLS LAST
|
|
127
|
-
LIMIT 50;
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
## Existing Page Coverage
|
|
131
|
-
|
|
132
|
-
```sql
|
|
133
|
-
SELECT
|
|
134
|
-
id,
|
|
135
|
-
path,
|
|
136
|
-
title,
|
|
137
|
-
description,
|
|
138
|
-
status,
|
|
139
|
-
"intentId",
|
|
140
|
-
"targetKeywords",
|
|
141
|
-
"targetSearchQueries",
|
|
142
|
-
archetype,
|
|
143
|
-
"lengthTargetWords",
|
|
144
|
-
"priorityScore",
|
|
145
|
-
"updatedAt"
|
|
146
|
-
FROM "Page"
|
|
147
|
-
WHERE "workspaceId" = '<workspaceId>'
|
|
148
|
-
AND status IN ('draft', 'preview_ready', 'deployed')
|
|
149
|
-
ORDER BY "updatedAt" DESC
|
|
150
|
-
LIMIT 100;
|
|
151
|
-
```
|
|
152
|
-
|
|
153
|
-
## Page Research Evidence
|
|
154
|
-
|
|
155
|
-
```sql
|
|
156
|
-
SELECT
|
|
157
|
-
id,
|
|
158
|
-
path,
|
|
159
|
-
title,
|
|
160
|
-
left("researchReport", 6000) AS research_report_excerpt,
|
|
161
|
-
"researchSources",
|
|
162
|
-
"researchedAt"
|
|
163
|
-
FROM "Page"
|
|
164
|
-
WHERE "workspaceId" = '<workspaceId>'
|
|
165
|
-
AND "researchReport" IS NOT NULL
|
|
166
|
-
ORDER BY "researchedAt" DESC NULLS LAST
|
|
167
|
-
LIMIT 20;
|
|
168
|
-
```
|
|
169
|
-
|
|
170
|
-
## Recent AI Answer Evidence
|
|
171
|
-
|
|
172
|
-
```sql
|
|
173
|
-
SELECT
|
|
174
|
-
air.id,
|
|
175
|
-
air.engine,
|
|
176
|
-
air.prompt,
|
|
177
|
-
left(air."responseText", 3000) AS response_excerpt,
|
|
178
|
-
air.citations,
|
|
179
|
-
air.mentions,
|
|
180
|
-
air."hasBrandMention",
|
|
181
|
-
air."createdAt"
|
|
182
|
-
FROM "AnalysisItemResult" air
|
|
183
|
-
WHERE air."workspaceId" = '<workspaceId>'
|
|
184
|
-
AND air.status = 'completed'
|
|
185
|
-
ORDER BY air."createdAt" DESC
|
|
186
|
-
LIMIT 50;
|
|
187
|
-
```
|