@agentsean/playbooks 1.0.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/LICENSE +661 -0
- package/dist/catalog.d.ts +10 -0
- package/dist/catalog.d.ts.map +1 -0
- package/dist/catalog.js +827 -0
- package/dist/catalog.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +37 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +47 -0
package/dist/catalog.js
ADDED
|
@@ -0,0 +1,827 @@
|
|
|
1
|
+
const OPENSEO = {
|
|
2
|
+
project: "openseo",
|
|
3
|
+
treatment: "ADAPT",
|
|
4
|
+
copyright: "Copyright (c) 2026 Ben Senescu and contributors",
|
|
5
|
+
};
|
|
6
|
+
export const PLAYBOOKS = [
|
|
7
|
+
{
|
|
8
|
+
id: "seo-audit",
|
|
9
|
+
version: "1.0.0",
|
|
10
|
+
title: "SEO audit",
|
|
11
|
+
summary: "Audit a domain and produce one plain-language action the owner can take this week, backed by verified live-page evidence.",
|
|
12
|
+
source: { ...OPENSEO, skill: "seo-audit" },
|
|
13
|
+
inputs: [
|
|
14
|
+
{
|
|
15
|
+
name: "origin",
|
|
16
|
+
type: "url",
|
|
17
|
+
required: true,
|
|
18
|
+
notes: "Site origin from the crawl table",
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
name: "findings",
|
|
22
|
+
type: "Finding[]",
|
|
23
|
+
required: true,
|
|
24
|
+
notes: "Prioritized catalogue findings",
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
name: "businessOverview",
|
|
28
|
+
type: "string",
|
|
29
|
+
required: false,
|
|
30
|
+
notes: "What the business does",
|
|
31
|
+
},
|
|
32
|
+
],
|
|
33
|
+
decisionRules: [
|
|
34
|
+
{
|
|
35
|
+
id: "one-thing",
|
|
36
|
+
when: "findings exist",
|
|
37
|
+
action: "Pick a single do-this-week action a non-technical person can complete",
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
id: "verify-live",
|
|
41
|
+
when: "a finding will be reported",
|
|
42
|
+
action: "Require evidence from crawled HTML; report nothing unverified",
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
id: "dead-domain",
|
|
46
|
+
when: "crawl is empty or certificate/5xx",
|
|
47
|
+
action: "Investigate successor domain before recommending content",
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
id: "skip-nitpicks",
|
|
51
|
+
when: "finding does not change the one thing",
|
|
52
|
+
action: "Drop it from the user-facing report",
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
outputSchema: {
|
|
56
|
+
type: "object",
|
|
57
|
+
required: ["verdict", "oneThing", "fixes"],
|
|
58
|
+
properties: {
|
|
59
|
+
verdict: { type: "string", description: "2-3 sentence state of the site" },
|
|
60
|
+
oneThing: { type: "string", description: "The single action for this week" },
|
|
61
|
+
fixes: { type: "array", description: "5-10 small fixes ordered by impact" },
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
guardrails: [
|
|
65
|
+
"Calm tone. No drama words.",
|
|
66
|
+
"Gloss jargon on first use.",
|
|
67
|
+
"Missing backlink data is 'no recorded data', not a penalty.",
|
|
68
|
+
],
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
id: "keyword-research",
|
|
72
|
+
version: "1.0.0",
|
|
73
|
+
title: "Keyword research",
|
|
74
|
+
summary: "Turn seeds and first-party GSC queries into a prioritized opportunity set. Default metric is clicks.",
|
|
75
|
+
source: { ...OPENSEO, skill: "keyword-research" },
|
|
76
|
+
inputs: [
|
|
77
|
+
{
|
|
78
|
+
name: "seeds",
|
|
79
|
+
type: "string[]",
|
|
80
|
+
required: true,
|
|
81
|
+
notes: "Topics, products, or pages",
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
name: "gscQueries",
|
|
85
|
+
type: "QueryDaily[]",
|
|
86
|
+
required: false,
|
|
87
|
+
notes: "First-party demand; prefer over third-party volume",
|
|
88
|
+
},
|
|
89
|
+
],
|
|
90
|
+
decisionRules: [
|
|
91
|
+
{
|
|
92
|
+
id: "gsc-first",
|
|
93
|
+
when: "GSC is connected",
|
|
94
|
+
action: "Work striking-distance queries (position 8-20) before broad discovery",
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
id: "clicks",
|
|
98
|
+
when: "ranking any metric",
|
|
99
|
+
action: "Use clicks; impressions 2025-05-13 to 2026-04-27 are contaminated",
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
id: "fit-over-volume",
|
|
103
|
+
when: "prioritizing",
|
|
104
|
+
action: "Business fit and intent beat raw volume",
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
id: "no-invent",
|
|
108
|
+
when: "a metric is missing",
|
|
109
|
+
action: "Write unknown; never invent numbers",
|
|
110
|
+
},
|
|
111
|
+
],
|
|
112
|
+
outputSchema: {
|
|
113
|
+
type: "object",
|
|
114
|
+
required: ["theme", "keywords"],
|
|
115
|
+
properties: {
|
|
116
|
+
theme: { type: "string", description: "Best opportunity theme" },
|
|
117
|
+
keywords: { type: "array", description: "Shortlist with intent and priority" },
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
guardrails: [
|
|
121
|
+
"Do not invent metrics.",
|
|
122
|
+
"Do not save keywords without confirmation.",
|
|
123
|
+
],
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
id: "keyword-clustering",
|
|
127
|
+
version: "1.0.0",
|
|
128
|
+
title: "Keyword clustering",
|
|
129
|
+
summary: "Group keywords by intent and map each cluster to an existing or proposed page.",
|
|
130
|
+
source: { ...OPENSEO, skill: "keyword-clustering" },
|
|
131
|
+
inputs: [
|
|
132
|
+
{ name: "keywords", type: "string[]", required: true, notes: "Candidate terms" },
|
|
133
|
+
{
|
|
134
|
+
name: "pages",
|
|
135
|
+
type: "Page[]",
|
|
136
|
+
required: true,
|
|
137
|
+
notes: "Existing URLs to map against",
|
|
138
|
+
},
|
|
139
|
+
],
|
|
140
|
+
decisionRules: [
|
|
141
|
+
{
|
|
142
|
+
id: "intent-wins",
|
|
143
|
+
when: "two terms look similar",
|
|
144
|
+
action: "Split if SERP intent or page type differs; do not cluster on lexical similarity alone",
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
id: "prefer-existing",
|
|
148
|
+
when: "an existing URL fits the cluster",
|
|
149
|
+
action: "Refresh that URL; do not mint a new page",
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
id: "cannibalization",
|
|
153
|
+
when: "one query hits multiple URLs",
|
|
154
|
+
action: "Flag consolidation; do not create another competing URL",
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
id: "tiny-set",
|
|
158
|
+
when: "fewer than 10 usable terms",
|
|
159
|
+
action: "Produce a simple map, not a cluster theatre",
|
|
160
|
+
},
|
|
161
|
+
],
|
|
162
|
+
outputSchema: {
|
|
163
|
+
type: "object",
|
|
164
|
+
required: ["clusters"],
|
|
165
|
+
properties: {
|
|
166
|
+
clusters: {
|
|
167
|
+
type: "array",
|
|
168
|
+
description: "Cluster, primary keyword, target page, page type",
|
|
169
|
+
},
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
guardrails: [
|
|
173
|
+
"Do not over-cluster tiny sets.",
|
|
174
|
+
"Label unmapped targets as proposed.",
|
|
175
|
+
],
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
id: "local-seo",
|
|
179
|
+
version: "1.0.0",
|
|
180
|
+
title: "Local SEO",
|
|
181
|
+
summary: "Audit a Google Business Profile against local competitors and map Maps visibility.",
|
|
182
|
+
source: { ...OPENSEO, skill: "local-seo" },
|
|
183
|
+
inputs: [
|
|
184
|
+
{ name: "business", type: "string", required: true, notes: "Name or place id" },
|
|
185
|
+
{
|
|
186
|
+
name: "keywords",
|
|
187
|
+
type: "string[]",
|
|
188
|
+
required: true,
|
|
189
|
+
notes: "1-3 customer search terms, not the brand",
|
|
190
|
+
},
|
|
191
|
+
],
|
|
192
|
+
decisionRules: [
|
|
193
|
+
{
|
|
194
|
+
id: "claim-first",
|
|
195
|
+
when: "profile is unclaimed or category is wrong",
|
|
196
|
+
action: "Fix claim and category before posting cadence",
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
id: "cid-match",
|
|
200
|
+
when: "matching listings",
|
|
201
|
+
action: "Match by cid or place_id, never by name alone",
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
id: "no-review-gating",
|
|
205
|
+
when: "review strategy is requested",
|
|
206
|
+
action: "Refuse gating, fake reviews, and keyword-stuffed names (T4)",
|
|
207
|
+
},
|
|
208
|
+
],
|
|
209
|
+
outputSchema: {
|
|
210
|
+
type: "object",
|
|
211
|
+
required: ["snapshot", "oneFix"],
|
|
212
|
+
properties: {
|
|
213
|
+
snapshot: { type: "object", description: "Category, rating, reviews, claimed" },
|
|
214
|
+
oneFix: { type: "string", description: "The one local fix this week" },
|
|
215
|
+
},
|
|
216
|
+
},
|
|
217
|
+
guardrails: [
|
|
218
|
+
"Never recommend review gating or fake reviews.",
|
|
219
|
+
"Do not infer local-pack strength from national organic metrics.",
|
|
220
|
+
],
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
id: "link-prospecting",
|
|
224
|
+
version: "1.0.0",
|
|
225
|
+
title: "Link prospecting",
|
|
226
|
+
summary: "Find realistic pages that might reference a linkable asset. Outreach send is T3.",
|
|
227
|
+
source: { ...OPENSEO, skill: "link-prospecting" },
|
|
228
|
+
inputs: [
|
|
229
|
+
{
|
|
230
|
+
name: "assetUrl",
|
|
231
|
+
type: "url",
|
|
232
|
+
required: true,
|
|
233
|
+
notes: "The page someone would cite",
|
|
234
|
+
},
|
|
235
|
+
{ name: "topic", type: "string", required: true, notes: "Why it is citeable" },
|
|
236
|
+
],
|
|
237
|
+
decisionRules: [
|
|
238
|
+
{
|
|
239
|
+
id: "mention-first",
|
|
240
|
+
when: "prioritizing off-page work",
|
|
241
|
+
action: "Unlinked brand mentions and inbound-404 recovery before cold outreach",
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
id: "no-invent-contacts",
|
|
245
|
+
when: "an email is not on the page",
|
|
246
|
+
action: "Do not invent contact details",
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
id: "t3-send",
|
|
250
|
+
when: "an outreach draft is ready",
|
|
251
|
+
action: "Queue send_outreach_email as T3; never auto-send",
|
|
252
|
+
},
|
|
253
|
+
],
|
|
254
|
+
outputSchema: {
|
|
255
|
+
type: "object",
|
|
256
|
+
required: ["prospects", "drafts"],
|
|
257
|
+
properties: {
|
|
258
|
+
prospects: { type: "array", description: "URL, angle, contact path" },
|
|
259
|
+
drafts: { type: "array", description: "Reusable outreach drafts; send is T3" },
|
|
260
|
+
},
|
|
261
|
+
},
|
|
262
|
+
guardrails: [
|
|
263
|
+
"Do not invent emails.",
|
|
264
|
+
"Avoid mass outreach.",
|
|
265
|
+
"Flag paid-placement prospects.",
|
|
266
|
+
],
|
|
267
|
+
},
|
|
268
|
+
{
|
|
269
|
+
id: "competitor-analysis",
|
|
270
|
+
version: "1.0.0",
|
|
271
|
+
title: "Competitor analysis",
|
|
272
|
+
summary: "Analyze one competitor deeply enough to decide what to learn from, avoid, or outrank.",
|
|
273
|
+
source: { ...OPENSEO, skill: "competitor-analysis" },
|
|
274
|
+
inputs: [
|
|
275
|
+
{
|
|
276
|
+
name: "competitorOrigin",
|
|
277
|
+
type: "url",
|
|
278
|
+
required: true,
|
|
279
|
+
notes: "Named competitor domain",
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
name: "ourOrigin",
|
|
283
|
+
type: "url",
|
|
284
|
+
required: false,
|
|
285
|
+
notes: "User domain for comparison",
|
|
286
|
+
},
|
|
287
|
+
],
|
|
288
|
+
decisionRules: [
|
|
289
|
+
{
|
|
290
|
+
id: "fit-filter",
|
|
291
|
+
when: "listing competitor keywords",
|
|
292
|
+
action: "Drop terms that are not a business fit",
|
|
293
|
+
},
|
|
294
|
+
{
|
|
295
|
+
id: "no-copy",
|
|
296
|
+
when: "recommending content",
|
|
297
|
+
action: "Recommend a stronger angle, never copying their copy",
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
id: "evidence",
|
|
301
|
+
when: "claiming a page-type pattern",
|
|
302
|
+
action: "Require crawled or SERP evidence, not keyword rows alone",
|
|
303
|
+
},
|
|
304
|
+
],
|
|
305
|
+
outputSchema: {
|
|
306
|
+
type: "object",
|
|
307
|
+
required: ["snapshot", "opportunity"],
|
|
308
|
+
properties: {
|
|
309
|
+
snapshot: { type: "string", description: "What they do well" },
|
|
310
|
+
opportunity: { type: "string", description: "Best opening to beat them" },
|
|
311
|
+
},
|
|
312
|
+
},
|
|
313
|
+
guardrails: [
|
|
314
|
+
"Separate evidence from inference.",
|
|
315
|
+
"Do not treat every competitor keyword as desirable.",
|
|
316
|
+
],
|
|
317
|
+
},
|
|
318
|
+
{
|
|
319
|
+
id: "competitive-landscape",
|
|
320
|
+
version: "1.0.0",
|
|
321
|
+
title: "Competitive landscape",
|
|
322
|
+
summary: "Who is winning this SEO market, what content is working, and where the openings are.",
|
|
323
|
+
source: { ...OPENSEO, skill: "competitive-landscape" },
|
|
324
|
+
inputs: [
|
|
325
|
+
{ name: "seeds", type: "string[]", required: true, notes: "Market queries" },
|
|
326
|
+
{ name: "competitors", type: "url[]", required: false, notes: "Known domains" },
|
|
327
|
+
],
|
|
328
|
+
decisionRules: [
|
|
329
|
+
{
|
|
330
|
+
id: "seo-vs-biz",
|
|
331
|
+
when: "labeling a domain",
|
|
332
|
+
action: "Distinguish SEO competitors from business competitors",
|
|
333
|
+
},
|
|
334
|
+
{
|
|
335
|
+
id: "small-set",
|
|
336
|
+
when: "query set is small",
|
|
337
|
+
action: "Call the read directional",
|
|
338
|
+
},
|
|
339
|
+
{
|
|
340
|
+
id: "next",
|
|
341
|
+
when: "an opening is found",
|
|
342
|
+
action: "Hand off to competitor-analysis, clustering, or content-brief",
|
|
343
|
+
},
|
|
344
|
+
],
|
|
345
|
+
outputSchema: {
|
|
346
|
+
type: "object",
|
|
347
|
+
required: ["leaders", "opening"],
|
|
348
|
+
properties: {
|
|
349
|
+
leaders: { type: "array", description: "Recurring ranking domains by type" },
|
|
350
|
+
opening: { type: "string", description: "Most winnable opportunity area" },
|
|
351
|
+
},
|
|
352
|
+
},
|
|
353
|
+
guardrails: [
|
|
354
|
+
"Do not overstate estimated traffic.",
|
|
355
|
+
"Label publishers vs product competitors.",
|
|
356
|
+
],
|
|
357
|
+
},
|
|
358
|
+
{
|
|
359
|
+
id: "seo-coach",
|
|
360
|
+
version: "1.0.0",
|
|
361
|
+
title: "SEO coach",
|
|
362
|
+
summary: "Pick one next workflow and explain it in plain language.",
|
|
363
|
+
source: { ...OPENSEO, skill: "seo-coach" },
|
|
364
|
+
inputs: [
|
|
365
|
+
{
|
|
366
|
+
name: "goal",
|
|
367
|
+
type: "string",
|
|
368
|
+
required: false,
|
|
369
|
+
notes: "What the user wants next",
|
|
370
|
+
},
|
|
371
|
+
{
|
|
372
|
+
name: "connected",
|
|
373
|
+
type: "string[]",
|
|
374
|
+
required: false,
|
|
375
|
+
notes: "GSC, GA4, CMS already connected",
|
|
376
|
+
},
|
|
377
|
+
],
|
|
378
|
+
decisionRules: [
|
|
379
|
+
{
|
|
380
|
+
id: "one-step",
|
|
381
|
+
when: "the user is unsure",
|
|
382
|
+
action: "Offer one next workflow, not a menu of ten",
|
|
383
|
+
},
|
|
384
|
+
{
|
|
385
|
+
id: "gsc-free",
|
|
386
|
+
when: "explaining data",
|
|
387
|
+
action: "GSC clicks are first-party and free; do not prefer paid volume",
|
|
388
|
+
},
|
|
389
|
+
{
|
|
390
|
+
id: "setup-first",
|
|
391
|
+
when: "business overview is empty",
|
|
392
|
+
action: "Recommend seo-project-setup",
|
|
393
|
+
},
|
|
394
|
+
],
|
|
395
|
+
outputSchema: {
|
|
396
|
+
type: "object",
|
|
397
|
+
required: ["next"],
|
|
398
|
+
properties: {
|
|
399
|
+
next: { type: "string", description: "The one recommended workflow id" },
|
|
400
|
+
},
|
|
401
|
+
},
|
|
402
|
+
guardrails: ["Do not overload beginners.", "Distinguish live data from judgment."],
|
|
403
|
+
},
|
|
404
|
+
{
|
|
405
|
+
id: "seo-project-setup",
|
|
406
|
+
version: "1.0.0",
|
|
407
|
+
title: "Project setup",
|
|
408
|
+
summary: "Capture business, goal, positioning, voice, competitors, and key pages once.",
|
|
409
|
+
source: { ...OPENSEO, skill: "seo-project-setup" },
|
|
410
|
+
inputs: [
|
|
411
|
+
{ name: "origin", type: "url", required: true, notes: "Primary domain" },
|
|
412
|
+
{
|
|
413
|
+
name: "voice",
|
|
414
|
+
type: "object",
|
|
415
|
+
required: false,
|
|
416
|
+
notes: "Writing preferences stored as the style profile",
|
|
417
|
+
},
|
|
418
|
+
],
|
|
419
|
+
decisionRules: [
|
|
420
|
+
{
|
|
421
|
+
id: "confirm",
|
|
422
|
+
when: "inferring from the site",
|
|
423
|
+
action: "Save only after the user confirms, or mark as inferred",
|
|
424
|
+
},
|
|
425
|
+
{
|
|
426
|
+
id: "voice-to-profile",
|
|
427
|
+
when: "banned phrases or tone are given",
|
|
428
|
+
action: "Write them to style_profiles, which the publish gate reads",
|
|
429
|
+
},
|
|
430
|
+
{
|
|
431
|
+
id: "gsc",
|
|
432
|
+
when: "onboarding",
|
|
433
|
+
action: "Offer Google connect after the first crawl, not before",
|
|
434
|
+
},
|
|
435
|
+
],
|
|
436
|
+
outputSchema: {
|
|
437
|
+
type: "object",
|
|
438
|
+
required: ["businessOverview", "currentGoal"],
|
|
439
|
+
properties: {
|
|
440
|
+
businessOverview: { type: "string", description: "What the business does" },
|
|
441
|
+
currentGoal: { type: "string", description: "Metric and timeframe" },
|
|
442
|
+
},
|
|
443
|
+
},
|
|
444
|
+
guardrails: [
|
|
445
|
+
"Keep setup lightweight.",
|
|
446
|
+
"Do not claim GSC is connected unless a connection row exists.",
|
|
447
|
+
],
|
|
448
|
+
},
|
|
449
|
+
{
|
|
450
|
+
id: "content-refresh",
|
|
451
|
+
version: "1.0.0",
|
|
452
|
+
title: "Content refresh",
|
|
453
|
+
summary: "Default content action: rewrite an existing URL rather than mint a new one. Cap 2 refreshes/day/site.",
|
|
454
|
+
inputs: [
|
|
455
|
+
{ name: "page", type: "Page", required: true, notes: "Crawled page to refresh" },
|
|
456
|
+
{
|
|
457
|
+
name: "decay",
|
|
458
|
+
type: "Decay",
|
|
459
|
+
required: false,
|
|
460
|
+
notes: "GSC clicks drop, 28 vs previous 28",
|
|
461
|
+
},
|
|
462
|
+
{
|
|
463
|
+
name: "brief",
|
|
464
|
+
type: "ContentBrief",
|
|
465
|
+
required: true,
|
|
466
|
+
notes: "From content-brief",
|
|
467
|
+
},
|
|
468
|
+
],
|
|
469
|
+
decisionRules: [
|
|
470
|
+
{
|
|
471
|
+
id: "prefer-refresh",
|
|
472
|
+
when: "an existing URL covers the intent",
|
|
473
|
+
action: "Emit refresh_content, never create_page",
|
|
474
|
+
},
|
|
475
|
+
{
|
|
476
|
+
id: "decay-clicks",
|
|
477
|
+
when: "selecting a candidate",
|
|
478
|
+
action: "Rank by click drop, not impression drop",
|
|
479
|
+
},
|
|
480
|
+
{
|
|
481
|
+
id: "thin",
|
|
482
|
+
when: "no decaying page exists",
|
|
483
|
+
action: "Fall back to thin pages (low word count) from the catalogue",
|
|
484
|
+
},
|
|
485
|
+
{
|
|
486
|
+
id: "cap",
|
|
487
|
+
when: "2 refreshes already applied today",
|
|
488
|
+
action: "Stop; the cap is not overridable",
|
|
489
|
+
},
|
|
490
|
+
],
|
|
491
|
+
outputSchema: {
|
|
492
|
+
type: "object",
|
|
493
|
+
required: ["kind", "targetUrl", "body"],
|
|
494
|
+
properties: {
|
|
495
|
+
kind: { type: "string", description: "Always refresh_content" },
|
|
496
|
+
targetUrl: { type: "url", description: "Existing crawled URL" },
|
|
497
|
+
body: { type: "string", description: "Rewritten markdown" },
|
|
498
|
+
},
|
|
499
|
+
},
|
|
500
|
+
guardrails: [
|
|
501
|
+
"Do not mint a new URL to 'fix' decay.",
|
|
502
|
+
"Do not claim the rewrite caused a recovery unless the change sits in a pre-registered experiment that has reached its analysis date (evidence ladder A–E; default E).",
|
|
503
|
+
],
|
|
504
|
+
},
|
|
505
|
+
{
|
|
506
|
+
id: "content-brief",
|
|
507
|
+
version: "1.0.0",
|
|
508
|
+
title: "Content brief",
|
|
509
|
+
summary: "Entity/term extraction, heading coverage, question coverage, internal-link targets, MarketMuse-style content score.",
|
|
510
|
+
inputs: [
|
|
511
|
+
{ name: "pageBody", type: "string", required: true, notes: "Current page text" },
|
|
512
|
+
{
|
|
513
|
+
name: "gscQueries",
|
|
514
|
+
type: "QueryDaily[]",
|
|
515
|
+
required: false,
|
|
516
|
+
notes: "Questions and terms already earning clicks",
|
|
517
|
+
},
|
|
518
|
+
{
|
|
519
|
+
name: "sitePages",
|
|
520
|
+
type: "Page[]",
|
|
521
|
+
required: true,
|
|
522
|
+
notes: "Internal-link graph",
|
|
523
|
+
},
|
|
524
|
+
],
|
|
525
|
+
decisionRules: [
|
|
526
|
+
{
|
|
527
|
+
id: "topics-50",
|
|
528
|
+
when: "scoring",
|
|
529
|
+
action: "Score = Σ min(2, mentions) over ≤50 topics, scaled 0-100",
|
|
530
|
+
},
|
|
531
|
+
{
|
|
532
|
+
id: "questions",
|
|
533
|
+
when: "GSC queries look like questions",
|
|
534
|
+
action: "Add them to question coverage",
|
|
535
|
+
},
|
|
536
|
+
{
|
|
537
|
+
id: "links",
|
|
538
|
+
when: "picking internal links",
|
|
539
|
+
action: "Only crawled same-site URLs; 2-5 targets",
|
|
540
|
+
},
|
|
541
|
+
{
|
|
542
|
+
id: "facts",
|
|
543
|
+
when: "the page states a number",
|
|
544
|
+
action: "Record it as a fact with the page URL as source",
|
|
545
|
+
},
|
|
546
|
+
],
|
|
547
|
+
outputSchema: {
|
|
548
|
+
type: "object",
|
|
549
|
+
required: [
|
|
550
|
+
"topics",
|
|
551
|
+
"headings",
|
|
552
|
+
"questions",
|
|
553
|
+
"internalLinks",
|
|
554
|
+
"facts",
|
|
555
|
+
"contentScore",
|
|
556
|
+
],
|
|
557
|
+
properties: {
|
|
558
|
+
topics: { type: "array", description: "Up to 50 terms" },
|
|
559
|
+
headings: { type: "array", description: "Required H2/H3 coverage" },
|
|
560
|
+
questions: { type: "array", description: "Question coverage" },
|
|
561
|
+
internalLinks: { type: "array", description: "Crawled pages to link" },
|
|
562
|
+
facts: { type: "array", description: "Numeric claims with source URLs" },
|
|
563
|
+
contentScore: { type: "number", description: "0-100" },
|
|
564
|
+
},
|
|
565
|
+
},
|
|
566
|
+
guardrails: [
|
|
567
|
+
"Do not invent facts.",
|
|
568
|
+
"Do not include off-site URLs as internal-link targets.",
|
|
569
|
+
],
|
|
570
|
+
},
|
|
571
|
+
{
|
|
572
|
+
id: "publish-gate",
|
|
573
|
+
version: "1.0.0",
|
|
574
|
+
title: "PublishGate",
|
|
575
|
+
summary: "Ten deterministic checks. All must pass before an Action is emitted.",
|
|
576
|
+
inputs: [
|
|
577
|
+
{ name: "draft", type: "ContentDraft", required: true, notes: "Candidate body" },
|
|
578
|
+
{
|
|
579
|
+
name: "brief",
|
|
580
|
+
type: "ContentBrief",
|
|
581
|
+
required: true,
|
|
582
|
+
notes: "Source of facts and links",
|
|
583
|
+
},
|
|
584
|
+
],
|
|
585
|
+
decisionRules: [
|
|
586
|
+
{
|
|
587
|
+
id: "all-ten",
|
|
588
|
+
when: "any check fails",
|
|
589
|
+
action: "Reject the draft; do not emit an Action",
|
|
590
|
+
},
|
|
591
|
+
{
|
|
592
|
+
id: "no-override",
|
|
593
|
+
when: "a user setting tries to skip a check",
|
|
594
|
+
action: "Ignore the setting",
|
|
595
|
+
},
|
|
596
|
+
],
|
|
597
|
+
outputSchema: {
|
|
598
|
+
type: "object",
|
|
599
|
+
required: ["ok", "checks"],
|
|
600
|
+
properties: {
|
|
601
|
+
ok: { type: "boolean", description: "True only if all ten pass" },
|
|
602
|
+
checks: { type: "array", description: "id, code, ok, detail" },
|
|
603
|
+
},
|
|
604
|
+
},
|
|
605
|
+
guardrails: [
|
|
606
|
+
"1 fact-check",
|
|
607
|
+
"2 near-duplicate",
|
|
608
|
+
"3 readability/structure",
|
|
609
|
+
"4 brand voice",
|
|
610
|
+
"5 internal links resolve",
|
|
611
|
+
"6 schema valid",
|
|
612
|
+
"7 banned substrings",
|
|
613
|
+
"8 new-page rate limit",
|
|
614
|
+
"9 vertical block",
|
|
615
|
+
"10 AI disclosure",
|
|
616
|
+
],
|
|
617
|
+
},
|
|
618
|
+
{
|
|
619
|
+
id: "brand-voice",
|
|
620
|
+
version: "1.0.0",
|
|
621
|
+
title: "Brand voice",
|
|
622
|
+
summary: "Per-site style profile the publish gate enforces.",
|
|
623
|
+
inputs: [
|
|
624
|
+
{
|
|
625
|
+
name: "profile",
|
|
626
|
+
type: "StyleProfile",
|
|
627
|
+
required: true,
|
|
628
|
+
notes: "Banned phrases, preferred terms, sentence length",
|
|
629
|
+
},
|
|
630
|
+
{ name: "draft", type: "string", required: true, notes: "Candidate body" },
|
|
631
|
+
],
|
|
632
|
+
decisionRules: [
|
|
633
|
+
{ id: "banned", when: "a banned phrase appears", action: "Fail the voice check" },
|
|
634
|
+
{
|
|
635
|
+
id: "preferred",
|
|
636
|
+
when: "a preferred term has a listed synonym",
|
|
637
|
+
action: "Require the preferred term",
|
|
638
|
+
},
|
|
639
|
+
],
|
|
640
|
+
outputSchema: {
|
|
641
|
+
type: "object",
|
|
642
|
+
required: ["ok", "hits"],
|
|
643
|
+
properties: {
|
|
644
|
+
ok: { type: "boolean", description: "Voice conformance" },
|
|
645
|
+
hits: { type: "array", description: "Banned or missing preferred terms" },
|
|
646
|
+
},
|
|
647
|
+
},
|
|
648
|
+
guardrails: ["Empty profile passes.", "Voice is data, not a prompt."],
|
|
649
|
+
},
|
|
650
|
+
{
|
|
651
|
+
id: "vertical-block",
|
|
652
|
+
version: "1.0.0",
|
|
653
|
+
title: "Vertical block",
|
|
654
|
+
summary: "YMYL and affiliate content generation is T4. No setting exists.",
|
|
655
|
+
inputs: [
|
|
656
|
+
{
|
|
657
|
+
name: "ymylCategory",
|
|
658
|
+
type: "string",
|
|
659
|
+
required: false,
|
|
660
|
+
notes: "sites.ymyl_category",
|
|
661
|
+
},
|
|
662
|
+
{
|
|
663
|
+
name: "kind",
|
|
664
|
+
type: "ActionKind",
|
|
665
|
+
required: true,
|
|
666
|
+
notes: "Proposed action kind",
|
|
667
|
+
},
|
|
668
|
+
],
|
|
669
|
+
decisionRules: [
|
|
670
|
+
{
|
|
671
|
+
id: "hard-block",
|
|
672
|
+
when: "category is ymyl or affiliate",
|
|
673
|
+
action: "Refuse refresh_content, create_page, generate_ymyl, generate_affiliate",
|
|
674
|
+
},
|
|
675
|
+
{
|
|
676
|
+
id: "t4",
|
|
677
|
+
when: "kind is generate_ymyl or generate_affiliate",
|
|
678
|
+
action: "Always refuse, even if category is empty",
|
|
679
|
+
},
|
|
680
|
+
],
|
|
681
|
+
outputSchema: {
|
|
682
|
+
type: "object",
|
|
683
|
+
required: ["blocked"],
|
|
684
|
+
properties: {
|
|
685
|
+
blocked: { type: "boolean", description: "True if generation must not run" },
|
|
686
|
+
},
|
|
687
|
+
},
|
|
688
|
+
guardrails: ["Not overridable.", "T4 kinds have no setting."],
|
|
689
|
+
},
|
|
690
|
+
{
|
|
691
|
+
id: "aeo-evidence",
|
|
692
|
+
version: "1.0.0",
|
|
693
|
+
title: "AEO extractable evidence",
|
|
694
|
+
summary: "Citation selection and answer absorption are distinct. High-impact pages are dense in definitions, numeric facts, comparisons, and procedures. Do not sell schema, length, or llms.txt as AEO levers.",
|
|
695
|
+
inputs: [
|
|
696
|
+
{ name: "origin", type: "url", required: true, notes: "Site origin" },
|
|
697
|
+
{ name: "pages", type: "Page[]", required: true, notes: "Crawled pages" },
|
|
698
|
+
],
|
|
699
|
+
decisionRules: [
|
|
700
|
+
{
|
|
701
|
+
id: "no-schema-lever",
|
|
702
|
+
when: "someone asks to add schema for AI citations",
|
|
703
|
+
action: "Refuse; Ahrefs DiD found no measurable effect",
|
|
704
|
+
},
|
|
705
|
+
{
|
|
706
|
+
id: "no-llms",
|
|
707
|
+
when: "someone asks to publish llms.txt for GEO",
|
|
708
|
+
action: "Optional toggle only; 97% of files are never fetched",
|
|
709
|
+
},
|
|
710
|
+
{
|
|
711
|
+
id: "evidence-dense",
|
|
712
|
+
when: "briefing a page for AI citation",
|
|
713
|
+
action: "Require definitions, numeric facts, comparisons, procedures",
|
|
714
|
+
},
|
|
715
|
+
],
|
|
716
|
+
outputSchema: {
|
|
717
|
+
type: "object",
|
|
718
|
+
required: ["spec"],
|
|
719
|
+
properties: {
|
|
720
|
+
spec: { type: "string", description: "Extractable-evidence content spec" },
|
|
721
|
+
},
|
|
722
|
+
},
|
|
723
|
+
guardrails: ["Training crawlers ≠ citation crawlers.", "robots.txt is T3."],
|
|
724
|
+
},
|
|
725
|
+
{
|
|
726
|
+
id: "local-gbp",
|
|
727
|
+
version: "1.0.0",
|
|
728
|
+
title: "Local GBP",
|
|
729
|
+
summary: "Manage a Google Business Profile within quota. AI citation gap over rank tracking. Never generate reviews or city×service pages.",
|
|
730
|
+
inputs: [
|
|
731
|
+
{
|
|
732
|
+
name: "locations",
|
|
733
|
+
type: "GbpLocation[]",
|
|
734
|
+
required: true,
|
|
735
|
+
notes: "Connected profiles",
|
|
736
|
+
},
|
|
737
|
+
],
|
|
738
|
+
decisionRules: [
|
|
739
|
+
{
|
|
740
|
+
id: "quota",
|
|
741
|
+
when: "writing GBP",
|
|
742
|
+
action: "Token-bucket 10 edits/min/profile, 300 QPM; degrade to read-only without approval",
|
|
743
|
+
},
|
|
744
|
+
{
|
|
745
|
+
id: "no-title",
|
|
746
|
+
when: "tempted to keyword-stuff the GBP title",
|
|
747
|
+
action: "Advisory only",
|
|
748
|
+
},
|
|
749
|
+
{ id: "t4-reviews", when: "review generation requested", action: "Refuse T4" },
|
|
750
|
+
],
|
|
751
|
+
outputSchema: {
|
|
752
|
+
type: "object",
|
|
753
|
+
required: ["gap"],
|
|
754
|
+
properties: { gap: { type: "string", description: "AI citation gap report" } },
|
|
755
|
+
},
|
|
756
|
+
guardrails: ["Quota starts at 0 QPM until Google approves Basic API Access."],
|
|
757
|
+
},
|
|
758
|
+
{
|
|
759
|
+
id: "brand-mentions",
|
|
760
|
+
version: "1.0.0",
|
|
761
|
+
title: "Off-page brand authority",
|
|
762
|
+
summary: "Mention-first. Branded web mentions correlate 0.656–0.709 with AI visibility vs 0.266–0.326 for Domain Rating.",
|
|
763
|
+
inputs: [{ name: "brand", type: "string", required: true, notes: "Brand token" }],
|
|
764
|
+
decisionRules: [
|
|
765
|
+
{
|
|
766
|
+
id: "unlinked",
|
|
767
|
+
when: "mention exists without a link",
|
|
768
|
+
action: "Queue as opportunity; draft outreach T3",
|
|
769
|
+
},
|
|
770
|
+
{
|
|
771
|
+
id: "inbound-404",
|
|
772
|
+
when: "our URL 404s with inbound links",
|
|
773
|
+
action: "Fix on our side autonomously",
|
|
774
|
+
},
|
|
775
|
+
{
|
|
776
|
+
id: "no-disavow",
|
|
777
|
+
when: "disavow requested",
|
|
778
|
+
action: "Lock unless a manual action exists",
|
|
779
|
+
},
|
|
780
|
+
],
|
|
781
|
+
outputSchema: {
|
|
782
|
+
type: "object",
|
|
783
|
+
required: ["opportunities"],
|
|
784
|
+
properties: {
|
|
785
|
+
opportunities: { type: "array", description: "Mention and 404 rows" },
|
|
786
|
+
},
|
|
787
|
+
},
|
|
788
|
+
guardrails: [
|
|
789
|
+
"Send requires per-message approval.",
|
|
790
|
+
"GSC has no links or disavow API.",
|
|
791
|
+
],
|
|
792
|
+
},
|
|
793
|
+
{
|
|
794
|
+
id: "hosted-packaging",
|
|
795
|
+
version: "1.0.0",
|
|
796
|
+
title: "Hosted packaging",
|
|
797
|
+
summary: "Self-host is $0. Cloud Starter $9/mo. Agency $249/mo is the business. BYOK is required. Prefer a customer-side connector over holding CMS write credentials.",
|
|
798
|
+
inputs: [{ name: "plan", type: "string", required: true, notes: "Plan id" }],
|
|
799
|
+
decisionRules: [
|
|
800
|
+
{
|
|
801
|
+
id: "byok",
|
|
802
|
+
when: "hosted tenant has no LLM key",
|
|
803
|
+
action: "Refuse to run generation; BYOK is not optional",
|
|
804
|
+
},
|
|
805
|
+
{
|
|
806
|
+
id: "site-cap",
|
|
807
|
+
when: "tenant is at plan.sites",
|
|
808
|
+
action: "Refuse add-site; upgrade",
|
|
809
|
+
},
|
|
810
|
+
{
|
|
811
|
+
id: "no-cms-keys",
|
|
812
|
+
when: "hosted control plane would store a CMS write token",
|
|
813
|
+
action: "Refuse; issue a connector pairing",
|
|
814
|
+
},
|
|
815
|
+
],
|
|
816
|
+
outputSchema: {
|
|
817
|
+
type: "object",
|
|
818
|
+
required: ["plan"],
|
|
819
|
+
properties: { plan: { type: "string", description: "Active plan id" } },
|
|
820
|
+
},
|
|
821
|
+
guardrails: [
|
|
822
|
+
"Google refresh tokens stay sensitive-scope (no CASA).",
|
|
823
|
+
"Rank tracking is weekly on Starter.",
|
|
824
|
+
],
|
|
825
|
+
},
|
|
826
|
+
];
|
|
827
|
+
//# sourceMappingURL=catalog.js.map
|