claude-plugin-wordpress-manager 2.12.2 → 2.13.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/.claude-plugin/plugin.json +8 -3
- package/CHANGELOG.md +55 -0
- package/docs/GUIDE.md +240 -1
- package/docs/VALIDATION.md +341 -0
- package/docs/plans/2026-03-02-content-framework-architecture.md +612 -0
- package/docs/plans/2026-03-02-content-framework-strategic-reflections.md +228 -0
- package/docs/plans/2026-03-02-content-intelligence-phase2.md +560 -0
- package/docs/plans/2026-03-02-content-pipeline-phase1.md +456 -0
- package/docs/plans/2026-03-02-editorial-calendar-phase3.md +490 -0
- package/docs/validation/.gitkeep +0 -0
- package/docs/validation/dashboard.html +286 -0
- package/docs/validation/results.json +1705 -0
- package/package.json +12 -3
- package/scripts/run-validation.mjs +1132 -0
- package/servers/wp-rest-bridge/build/server.js +16 -5
- package/servers/wp-rest-bridge/build/tools/index.js +0 -9
- package/servers/wp-rest-bridge/build/tools/plugin-repository.js +23 -31
- package/servers/wp-rest-bridge/build/tools/schema.js +10 -2
- package/servers/wp-rest-bridge/build/tools/unified-content.js +10 -2
- package/servers/wp-rest-bridge/build/wordpress.d.ts +0 -3
- package/servers/wp-rest-bridge/build/wordpress.js +16 -98
- package/servers/wp-rest-bridge/package.json +1 -0
- package/skills/wp-analytics/SKILL.md +153 -0
- package/skills/wp-analytics/references/signals-feed-schema.md +417 -0
- package/skills/wp-content-pipeline/SKILL.md +461 -0
- package/skills/wp-content-pipeline/references/content-brief-schema.md +377 -0
- package/skills/wp-content-pipeline/references/site-config-schema.md +431 -0
- package/skills/wp-editorial-planner/SKILL.md +262 -0
- package/skills/wp-editorial-planner/references/editorial-schema.md +268 -0
|
@@ -0,0 +1,431 @@
|
|
|
1
|
+
# Site Configuration Schema
|
|
2
|
+
|
|
3
|
+
Schema reference for `.config.md` files -- per-site configuration that the `wp-content-pipeline` skill reads when processing briefs.
|
|
4
|
+
|
|
5
|
+
Config files live in `.content-state/` with the naming pattern `{site_id}.config.md`. They are gitignored because they contain site-specific values (profile IDs, audience IDs) that vary per environment.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## File Format
|
|
10
|
+
|
|
11
|
+
Each `.config.md` file consists of:
|
|
12
|
+
|
|
13
|
+
1. **YAML frontmatter** between `---` delimiters (structured configuration)
|
|
14
|
+
2. **Markdown body** after the closing `---` (free-form notes for Claude context)
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
---
|
|
18
|
+
site_id: opencactus
|
|
19
|
+
site_url: https://opencactus.com
|
|
20
|
+
last_updated: 2026-03-02
|
|
21
|
+
# ... other fields ...
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Notes
|
|
25
|
+
|
|
26
|
+
Free-form context that Claude uses when generating or adapting content for this site...
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Frontmatter Fields
|
|
32
|
+
|
|
33
|
+
### `site_id`
|
|
34
|
+
|
|
35
|
+
| Property | Value |
|
|
36
|
+
|----------|-------|
|
|
37
|
+
| Type | `string` |
|
|
38
|
+
| Required | **Yes** |
|
|
39
|
+
| Format | Lowercase alphanumeric with hyphens |
|
|
40
|
+
| Example | `opencactus` |
|
|
41
|
+
|
|
42
|
+
Unique identifier for the site. **Must match** the `id` field in the `WP_SITES_CONFIG` environment variable JSON array. This is how the pipeline resolves WordPress credentials (URL, username, app password) for API calls.
|
|
43
|
+
|
|
44
|
+
### `site_url`
|
|
45
|
+
|
|
46
|
+
| Property | Value |
|
|
47
|
+
|----------|-------|
|
|
48
|
+
| Type | `string` (URL) |
|
|
49
|
+
| Required | **Yes** |
|
|
50
|
+
| Format | `https://example.com` (no trailing slash) |
|
|
51
|
+
| Example | `https://opencactus.com` |
|
|
52
|
+
|
|
53
|
+
The public-facing URL of the WordPress site. Used for constructing internal links and verifying published content. Should match the `url` field in `WP_SITES_CONFIG`.
|
|
54
|
+
|
|
55
|
+
### `last_updated`
|
|
56
|
+
|
|
57
|
+
| Property | Value |
|
|
58
|
+
|----------|-------|
|
|
59
|
+
| Type | `string` (ISO 8601 date) |
|
|
60
|
+
| Required | No |
|
|
61
|
+
| Default | Date of file creation |
|
|
62
|
+
| Example | `2026-03-02` |
|
|
63
|
+
|
|
64
|
+
Date when this config was last reviewed or modified. Helps track staleness -- configs older than 90 days should be reviewed for accuracy.
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
### `brand` block
|
|
69
|
+
|
|
70
|
+
Defines the brand voice and editorial identity for the site. These values provide Claude with the context needed to generate on-brand content.
|
|
71
|
+
|
|
72
|
+
When `GenBrand` produces brand analysis output, those results map directly into this block. See [Integration Notes](#integration-notes) for the mapping.
|
|
73
|
+
|
|
74
|
+
```yaml
|
|
75
|
+
brand:
|
|
76
|
+
tone: professional, accessible, sustainability-focused
|
|
77
|
+
language: it
|
|
78
|
+
style_notes: |
|
|
79
|
+
Voice: warm but authoritative. Avoid corporate jargon.
|
|
80
|
+
Always emphasize the Sicilian heritage and natural ingredients.
|
|
81
|
+
Use "noi" (we) when referring to the company.
|
|
82
|
+
Sustainability is a core value, not a marketing angle.
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
| Field | Type | Required | Default | Description |
|
|
86
|
+
|-------|------|----------|---------|-------------|
|
|
87
|
+
| `tone` | `string` | **Yes** | -- | Comma-separated tone descriptors. Used by Claude to calibrate writing style. Examples: `professional, warm`, `casual, witty`, `technical, precise` |
|
|
88
|
+
| `language` | `string` | **Yes** | -- | ISO 639-1 language code for the site's primary content language. Affects content generation, SEO, and readability scoring |
|
|
89
|
+
| `style_notes` | `string` (multi-line) | No | `null` | Free-form editorial guidelines in YAML literal block scalar (`|`) format. Can include voice rules, banned words, preferred terminology, formatting conventions. No length limit |
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
### `defaults` block
|
|
94
|
+
|
|
95
|
+
Default values applied to briefs when the brief does not specify them explicitly. Brief-level values always override these defaults.
|
|
96
|
+
|
|
97
|
+
```yaml
|
|
98
|
+
defaults:
|
|
99
|
+
content_type: post
|
|
100
|
+
status: draft
|
|
101
|
+
categories:
|
|
102
|
+
- blog
|
|
103
|
+
author: editorial-team
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
| Field | Type | Required | Default | Description |
|
|
107
|
+
|-------|------|----------|---------|-------------|
|
|
108
|
+
| `content_type` | `string` | No | `post` | Default WordPress content type: `post`, `page`, or any registered custom post type |
|
|
109
|
+
| `status` | `string` | No | `draft` | Default WordPress post status: `draft`, `pending`, `publish`, `future`, `private` |
|
|
110
|
+
| `categories` | `string[]` | No | `[]` | Default category slugs applied when a brief omits categories |
|
|
111
|
+
| `author` | `string` | No | `null` | Default WordPress username or slug for post attribution. If `null`, uses the authenticated user from `WP_SITES_CONFIG` |
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
### `channels` block
|
|
116
|
+
|
|
117
|
+
Configures external distribution channels for the site. Each sub-key is a channel name with its own configuration. The pipeline reads this block to determine which channels are available and their credentials.
|
|
118
|
+
|
|
119
|
+
```yaml
|
|
120
|
+
channels:
|
|
121
|
+
linkedin:
|
|
122
|
+
enabled: true
|
|
123
|
+
profile_id: "urn:li:person:AbCdEf123"
|
|
124
|
+
format: professional
|
|
125
|
+
twitter:
|
|
126
|
+
enabled: true
|
|
127
|
+
format: concise
|
|
128
|
+
buffer:
|
|
129
|
+
enabled: false
|
|
130
|
+
profile_id: "buf_profile_abc123"
|
|
131
|
+
format: casual
|
|
132
|
+
mailchimp:
|
|
133
|
+
enabled: true
|
|
134
|
+
audience_id: "mc_aud_xyz789"
|
|
135
|
+
segment: newsletter-subscribers
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
#### Channel: `linkedin`
|
|
139
|
+
|
|
140
|
+
| Field | Type | Required | Default | Description |
|
|
141
|
+
|-------|------|----------|---------|-------------|
|
|
142
|
+
| `enabled` | `boolean` | **Yes** | -- | Whether LinkedIn distribution is active for this site |
|
|
143
|
+
| `profile_id` | `string` | Yes (if enabled) | -- | LinkedIn profile URN. Required by `li_create_post` MCP tool. Format: `urn:li:person:XXXXX` or `urn:li:organization:XXXXX` |
|
|
144
|
+
| `format` | `string` | No | `professional` | Content adaptation style: `professional`, `thought-leadership`, `casual` |
|
|
145
|
+
|
|
146
|
+
#### Channel: `twitter`
|
|
147
|
+
|
|
148
|
+
| Field | Type | Required | Default | Description |
|
|
149
|
+
|-------|------|----------|---------|-------------|
|
|
150
|
+
| `enabled` | `boolean` | **Yes** | -- | Whether Twitter/X distribution is active for this site |
|
|
151
|
+
| `format` | `string` | No | `concise` | Content adaptation style: `concise`, `thread`, `conversational` |
|
|
152
|
+
|
|
153
|
+
**Note:** Twitter tools (`tw_create_tweet`, `tw_create_thread`) authenticate via the MCP server configuration, so no `profile_id` is needed here.
|
|
154
|
+
|
|
155
|
+
#### Channel: `buffer`
|
|
156
|
+
|
|
157
|
+
| Field | Type | Required | Default | Description |
|
|
158
|
+
|-------|------|----------|---------|-------------|
|
|
159
|
+
| `enabled` | `boolean` | **Yes** | -- | Whether Buffer distribution is active for this site |
|
|
160
|
+
| `profile_id` | `string` | Yes (if enabled) | -- | Buffer profile ID. Required by `buf_create_update` MCP tool |
|
|
161
|
+
| `format` | `string` | No | `casual` | Content adaptation style: `professional`, `casual`, `promotional` |
|
|
162
|
+
|
|
163
|
+
#### Channel: `mailchimp`
|
|
164
|
+
|
|
165
|
+
| Field | Type | Required | Default | Description |
|
|
166
|
+
|-------|------|----------|---------|-------------|
|
|
167
|
+
| `enabled` | `boolean` | **Yes** | -- | Whether Mailchimp distribution is active for this site |
|
|
168
|
+
| `audience_id` | `string` | Yes (if enabled) | -- | Mailchimp audience/list ID. Required by `mc_create_campaign` MCP tool |
|
|
169
|
+
| `segment` | `string` | No | `null` | Mailchimp segment or tag to target within the audience. If `null`, sends to the full audience |
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
### `seo` block
|
|
174
|
+
|
|
175
|
+
Site-level SEO defaults. These apply when a brief does not specify its own SEO parameters. Brief-level `seo` values always override these.
|
|
176
|
+
|
|
177
|
+
```yaml
|
|
178
|
+
seo:
|
|
179
|
+
default_schema: Article
|
|
180
|
+
min_score: 70
|
|
181
|
+
auto_internal_links: true
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
| Field | Type | Required | Default | Description |
|
|
185
|
+
|-------|------|----------|---------|-------------|
|
|
186
|
+
| `default_schema` | `string` | No | `Article` | Default JSON-LD schema type for content: `Article`, `BlogPosting`, `HowTo`, `FAQPage`, `Product`, `Recipe`, `NewsArticle` |
|
|
187
|
+
| `min_score` | `integer` (0-100) | No | `70` | Default value for `brief.gates.seo_score_min` when the brief omits that field. Does not enforce a gate directly -- the gate is enforced at the brief level |
|
|
188
|
+
| `auto_internal_links` | `boolean` | No | `true` | Automatically discover and suggest internal links based on existing site content. When `true`, the pipeline scans the site's published posts to find relevant linking opportunities |
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
### `cadence` block
|
|
193
|
+
|
|
194
|
+
Editorial calendar configuration. Used by Phase 3 planning features. In Phase 1, the pipeline reads `publish_time` as default when `target.scheduled_date` is set without an explicit time.
|
|
195
|
+
|
|
196
|
+
```yaml
|
|
197
|
+
cadence:
|
|
198
|
+
posts_per_week: 3
|
|
199
|
+
preferred_days:
|
|
200
|
+
- monday
|
|
201
|
+
- wednesday
|
|
202
|
+
- friday
|
|
203
|
+
publish_time: "09:00"
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
| Field | Type | Required | Default | Description |
|
|
207
|
+
|-------|------|----------|---------|-------------|
|
|
208
|
+
| `posts_per_week` | `integer` | No | `2` | Target number of posts per week. Informs editorial planning and capacity alerts |
|
|
209
|
+
| `preferred_days` | `string[]` | No | `["monday", "thursday"]` | Preferred days of the week for publishing. Lowercase English day names |
|
|
210
|
+
| `publish_time` | `string` | No | `"09:00"` | Default publish time in `HH:MM` format (24-hour, site's local timezone). Applied when scheduling future posts without an explicit time |
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
## Body Section
|
|
215
|
+
|
|
216
|
+
The body section (after the closing `---`) is free-form Markdown that provides Claude with additional context about the site. This content is read by Claude when generating or adapting content but is not parsed as structured data.
|
|
217
|
+
|
|
218
|
+
Recommended content for the body section:
|
|
219
|
+
|
|
220
|
+
- **Brand story** -- brief narrative that Claude can reference for tone consistency
|
|
221
|
+
- **Product line** -- key products/services and how to reference them
|
|
222
|
+
- **Competitor notes** -- what to avoid saying, differentiation points
|
|
223
|
+
- **Seasonal considerations** -- recurring themes, campaigns, events
|
|
224
|
+
- **Terminology** -- preferred terms, abbreviations, translations
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
## Validation Rules
|
|
229
|
+
|
|
230
|
+
### Required Fields
|
|
231
|
+
|
|
232
|
+
These fields **must** be present for a config file to be valid:
|
|
233
|
+
|
|
234
|
+
| Field | Reason |
|
|
235
|
+
|-------|--------|
|
|
236
|
+
| `site_id` | Links config to `WP_SITES_CONFIG` credentials |
|
|
237
|
+
| `site_url` | Required for link construction and verification |
|
|
238
|
+
| `brand.tone` | Minimum brand voice definition |
|
|
239
|
+
| `brand.language` | Content language for generation and SEO |
|
|
240
|
+
|
|
241
|
+
### Channel Validation
|
|
242
|
+
|
|
243
|
+
For each channel where `enabled: true`:
|
|
244
|
+
|
|
245
|
+
| Channel | Required Field | MCP Tool |
|
|
246
|
+
|---------|---------------|----------|
|
|
247
|
+
| `linkedin` | `profile_id` | `li_create_post` |
|
|
248
|
+
| `buffer` | `profile_id` | `buf_create_update` |
|
|
249
|
+
| `mailchimp` | `audience_id` | `mc_create_campaign` |
|
|
250
|
+
| `twitter` | *(none)* | `tw_create_tweet`, `tw_create_thread` |
|
|
251
|
+
|
|
252
|
+
### Override Hierarchy
|
|
253
|
+
|
|
254
|
+
Brief-level values always take precedence over site config defaults:
|
|
255
|
+
|
|
256
|
+
```
|
|
257
|
+
Brief value > Site config default > System default
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
Specific overrides:
|
|
261
|
+
- `brief.target.content_type` > `config.defaults.content_type`
|
|
262
|
+
- `brief.target.status` > `config.defaults.status`
|
|
263
|
+
- `brief.target.categories` > `config.defaults.categories`
|
|
264
|
+
- `brief.content.author` > `config.defaults.author`
|
|
265
|
+
- `brief.seo.schema_type` > `config.seo.default_schema`
|
|
266
|
+
- `brief.gates.seo_score_min` > `config.seo.min_score`
|
|
267
|
+
- `brief.distribution.channels` -- selects which enabled channels to use; cannot activate a channel the config has `enabled: false`
|
|
268
|
+
|
|
269
|
+
---
|
|
270
|
+
|
|
271
|
+
## Integration Notes
|
|
272
|
+
|
|
273
|
+
### GenBrand Output Mapping
|
|
274
|
+
|
|
275
|
+
The `GenBrand` skill produces brand analysis that maps to the `brand` block:
|
|
276
|
+
|
|
277
|
+
| GenBrand Output | Config Field | Notes |
|
|
278
|
+
|-----------------|-------------|-------|
|
|
279
|
+
| Voice/tone descriptors | `brand.tone` | Comma-separated list of tone attributes |
|
|
280
|
+
| Primary language | `brand.language` | ISO 639-1 code |
|
|
281
|
+
| Editorial guidelines | `brand.style_notes` | Multi-line YAML block scalar. Include voice rules, banned words, preferred terminology |
|
|
282
|
+
| Brand narrative | Body section | Free-form context for Claude |
|
|
283
|
+
|
|
284
|
+
After running `GenBrand`, update the config file with the output. The `brand` block captures the structured attributes; the body section captures the narrative context.
|
|
285
|
+
|
|
286
|
+
### WP_SITES_CONFIG Mapping
|
|
287
|
+
|
|
288
|
+
The `WP_SITES_CONFIG` environment variable is a JSON array of site credentials:
|
|
289
|
+
|
|
290
|
+
```json
|
|
291
|
+
[
|
|
292
|
+
{
|
|
293
|
+
"id": "opencactus",
|
|
294
|
+
"url": "https://opencactus.com",
|
|
295
|
+
"username": "api-user",
|
|
296
|
+
"app_password": "xxxx xxxx xxxx xxxx"
|
|
297
|
+
}
|
|
298
|
+
]
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
The mapping between `WP_SITES_CONFIG` and the config file:
|
|
302
|
+
|
|
303
|
+
| WP_SITES_CONFIG Field | Config Field | Relationship |
|
|
304
|
+
|----------------------|-------------|--------------|
|
|
305
|
+
| `id` | `site_id` | **Must match exactly** -- this is the join key |
|
|
306
|
+
| `url` | `site_url` | Should match; config value used for public-facing links |
|
|
307
|
+
| `username` | `defaults.author` | Can differ; config author is for attribution, WP_SITES_CONFIG username is for API authentication |
|
|
308
|
+
| `app_password` | *(none)* | Credentials are never stored in config files |
|
|
309
|
+
|
|
310
|
+
**Important:** The config file never contains authentication credentials. All sensitive values remain in the `WP_SITES_CONFIG` environment variable.
|
|
311
|
+
|
|
312
|
+
### Brief Cross-Reference
|
|
313
|
+
|
|
314
|
+
When the pipeline processes a brief, it resolves the site config via `brief.target.site_id`:
|
|
315
|
+
|
|
316
|
+
1. Read `brief.target.site_id` (e.g., `opencactus`)
|
|
317
|
+
2. Load `.content-state/opencactus.config.md`
|
|
318
|
+
3. Apply config defaults for any fields the brief omits
|
|
319
|
+
4. Use `brand` block for content adaptation/generation
|
|
320
|
+
5. Use `channels` block for distribution routing
|
|
321
|
+
|
|
322
|
+
---
|
|
323
|
+
|
|
324
|
+
## Example Config
|
|
325
|
+
|
|
326
|
+
A complete `.config.md` for the opencactus site:
|
|
327
|
+
|
|
328
|
+
```markdown
|
|
329
|
+
---
|
|
330
|
+
site_id: opencactus
|
|
331
|
+
site_url: https://opencactus.com
|
|
332
|
+
last_updated: 2026-03-02
|
|
333
|
+
|
|
334
|
+
brand:
|
|
335
|
+
tone: professional, accessible, sustainability-focused
|
|
336
|
+
language: it
|
|
337
|
+
style_notes: |
|
|
338
|
+
Voice: warm but authoritative. Avoid corporate jargon.
|
|
339
|
+
Always emphasize the Sicilian heritage and natural ingredients.
|
|
340
|
+
Use "noi" (we) when referring to the company.
|
|
341
|
+
Sustainability is a core value, not a marketing angle -- weave it naturally.
|
|
342
|
+
Product names are always capitalized: Poco Dolce, Dolce, Molto Dolce.
|
|
343
|
+
Refer to the fruit as "fico d'India" (not "cactus" in Italian content).
|
|
344
|
+
Scientific claims must cite specific compounds (betalaine, polifenoli).
|
|
345
|
+
|
|
346
|
+
defaults:
|
|
347
|
+
content_type: post
|
|
348
|
+
status: draft
|
|
349
|
+
categories:
|
|
350
|
+
- blog
|
|
351
|
+
author: editorial-team
|
|
352
|
+
|
|
353
|
+
channels:
|
|
354
|
+
linkedin:
|
|
355
|
+
enabled: true
|
|
356
|
+
profile_id: "urn:li:organization:opencactus"
|
|
357
|
+
format: professional
|
|
358
|
+
twitter:
|
|
359
|
+
enabled: true
|
|
360
|
+
format: concise
|
|
361
|
+
buffer:
|
|
362
|
+
enabled: false
|
|
363
|
+
profile_id: ""
|
|
364
|
+
format: casual
|
|
365
|
+
mailchimp:
|
|
366
|
+
enabled: true
|
|
367
|
+
audience_id: "mc_aud_opencactus_main"
|
|
368
|
+
segment: newsletter-subscribers
|
|
369
|
+
|
|
370
|
+
seo:
|
|
371
|
+
default_schema: Article
|
|
372
|
+
min_score: 75
|
|
373
|
+
auto_internal_links: true
|
|
374
|
+
|
|
375
|
+
cadence:
|
|
376
|
+
posts_per_week: 3
|
|
377
|
+
preferred_days:
|
|
378
|
+
- monday
|
|
379
|
+
- wednesday
|
|
380
|
+
- friday
|
|
381
|
+
publish_time: "09:00"
|
|
382
|
+
---
|
|
383
|
+
|
|
384
|
+
## Brand Context
|
|
385
|
+
|
|
386
|
+
OpenCactus is the digital home of DolceZero, an Italian zero-calorie beverage brand based on Sicilian cactus water (acqua di fico d'India). The brand sits at the intersection of traditional Sicilian agriculture and modern wellness.
|
|
387
|
+
|
|
388
|
+
### Product Line
|
|
389
|
+
|
|
390
|
+
- **Poco Dolce** -- Light sweetness, subtle cactus flavor. Entry-level product.
|
|
391
|
+
- **Dolce** -- Medium sweetness, balanced flavor profile. The core product.
|
|
392
|
+
- **Molto Dolce** -- Full sweetness, rich cactus flavor. For those who prefer bolder taste.
|
|
393
|
+
|
|
394
|
+
All variants are zero-calorie, naturally flavored, with no artificial sweeteners.
|
|
395
|
+
|
|
396
|
+
### Key Differentiators
|
|
397
|
+
|
|
398
|
+
- Only cactus water brand with full Sicilian supply chain
|
|
399
|
+
- Zero calorie without artificial sweeteners (uses natural cactus compounds)
|
|
400
|
+
- 85% lower water footprint than conventional beverages
|
|
401
|
+
- Rich in betalains and polyphenols (natural antioxidants)
|
|
402
|
+
|
|
403
|
+
### Content Themes
|
|
404
|
+
|
|
405
|
+
- Sustainability and environmental responsibility
|
|
406
|
+
- Sicilian heritage and terroir
|
|
407
|
+
- Health and wellness (zero-calorie, natural ingredients)
|
|
408
|
+
- Innovation in food technology
|
|
409
|
+
- Community and local farmers
|
|
410
|
+
|
|
411
|
+
### Competitor Positioning
|
|
412
|
+
|
|
413
|
+
Avoid direct competitor mentions. Focus on DolceZero's unique attributes rather than comparison. Never claim "best" or "only" without substantiation.
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
---
|
|
417
|
+
|
|
418
|
+
## File Naming Convention
|
|
419
|
+
|
|
420
|
+
Config files follow this naming pattern:
|
|
421
|
+
|
|
422
|
+
```
|
|
423
|
+
{site_id}.config.md
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
Examples:
|
|
427
|
+
- `opencactus.config.md`
|
|
428
|
+
- `my-blog.config.md`
|
|
429
|
+
- `corporate-site.config.md`
|
|
430
|
+
|
|
431
|
+
Files are stored in `.content-state/` and are gitignored (site-specific configuration).
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: wp-editorial-planner
|
|
3
|
+
description: This skill should be used when the user asks to "create an editorial
|
|
4
|
+
plan", "update the calendar", "schedule posts", "plan content for March",
|
|
5
|
+
"convert calendar to briefs", "sync calendar with WordPress", "show editorial
|
|
6
|
+
status", or mentions planning content over time. Orchestrates monthly editorial
|
|
7
|
+
calendars that bridge signals intelligence to content publishing.
|
|
8
|
+
version: 1.0.0
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# WordPress Editorial Planner Skill
|
|
12
|
+
|
|
13
|
+
## Overview
|
|
14
|
+
|
|
15
|
+
The editorial planner manages monthly content calendars as `.state.md` files stored in `.content-state/`. It reads site configuration for publishing cadence, optionally consumes the signals feed generated by `wp-analytics` for data-driven topic ideas, and produces structured briefs that flow into the content pipeline for WordPress publishing. The workflow follows four sequential steps: **PLAN** (create or update the monthly calendar), **BRIEF** (convert planned entries into pipeline brief files), **SCHEDULE** (create WordPress future posts from ready briefs), and **SYNC** (synchronize WordPress publish status back to the calendar). Each step is idempotent and can be run independently.
|
|
16
|
+
|
|
17
|
+
## When to Use
|
|
18
|
+
|
|
19
|
+
- User wants to create a new monthly editorial plan
|
|
20
|
+
- User asks to update or view the current calendar
|
|
21
|
+
- User wants to convert planned entries into content briefs
|
|
22
|
+
- User asks to schedule ready briefs as WordPress future posts
|
|
23
|
+
- User wants to sync WordPress publish status back to the calendar
|
|
24
|
+
- User mentions "piano editoriale", "calendario", "schedula"
|
|
25
|
+
|
|
26
|
+
## Workflow Overview
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
PLAN --> BRIEF --> SCHEDULE --> SYNC
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Step 1: PLAN -- Create or update editorial calendar
|
|
35
|
+
|
|
36
|
+
**When to run:** User asks to create a new plan or update an existing one.
|
|
37
|
+
|
|
38
|
+
**Procedure:**
|
|
39
|
+
|
|
40
|
+
1. **Read site configuration** from `.content-state/{site_id}.config.md`:
|
|
41
|
+
- `cadence.posts_per_week` -- calculate `goals.posts_target` as `posts_per_week * weeks_in_month`
|
|
42
|
+
- `cadence.preferred_days` -- determine which weekdays to assign calendar entries
|
|
43
|
+
- `cadence.publish_time` -- note for the SCHEDULE step
|
|
44
|
+
- `defaults.categories`, `defaults.content_type` -- pre-fill the entry Tipo column
|
|
45
|
+
|
|
46
|
+
2. **Read signals feed** from `.content-state/signals-feed.md` if it exists:
|
|
47
|
+
- Extract anomalies with action containing "content cluster" or "Investigate"
|
|
48
|
+
- Suggest these as topics for `[da assegnare]` entries
|
|
49
|
+
- Present suggestions to user for approval before inserting into the calendar
|
|
50
|
+
|
|
51
|
+
3. **Optional strategic planning:** If the user wants a higher-level content strategy, suggest invoking GenMarketing for content calendar strategy development before populating individual entries.
|
|
52
|
+
|
|
53
|
+
4. **Generate or update** `.content-state/{YYYY-MM}-editorial.state.md`:
|
|
54
|
+
- Create YAML frontmatter with `calendar_id`, `site_id`, `period`, `goals`
|
|
55
|
+
- Create weekly tables with one row per `preferred_day`
|
|
56
|
+
- Fill known titles where provided, leave others as `[da assegnare]`
|
|
57
|
+
- Set all new entries as `status: planned`
|
|
58
|
+
|
|
59
|
+
5. **Present the calendar** to the user for review before writing to disk.
|
|
60
|
+
|
|
61
|
+
**Safety rules:**
|
|
62
|
+
- ALWAYS show the generated calendar to the user before writing the file
|
|
63
|
+
- If a calendar for the month already exists, show the diff and ask before overwriting
|
|
64
|
+
- Preserve existing entries that have `status: draft` or higher -- never reset them to `planned`
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## Step 2: BRIEF -- Convert calendar entries to brief files
|
|
69
|
+
|
|
70
|
+
**When to run:** User asks to "create briefs from calendar" or "convert planned entries to briefs".
|
|
71
|
+
|
|
72
|
+
**Procedure:**
|
|
73
|
+
|
|
74
|
+
1. Read the current editorial calendar `.state.md` file.
|
|
75
|
+
|
|
76
|
+
2. For each entry with `status: planned` AND a defined title (not `[da assegnare]`):
|
|
77
|
+
a. Generate a new `brief_id` as `BRF-YYYY-NNN` (sequential; scan existing briefs in both `pipeline-active/` and `pipeline-archive/` to determine the next number)
|
|
78
|
+
b. Read `.content-state/{site_id}.config.md` for site defaults
|
|
79
|
+
c. Create `.content-state/pipeline-active/{brief_id}.brief.md` with:
|
|
80
|
+
- `source.skill: wp-editorial-planner`
|
|
81
|
+
- `source.domain: editorial-calendar`
|
|
82
|
+
- `target.site_id`: from calendar `site_id`
|
|
83
|
+
- `target.content_type`: from entry Tipo column
|
|
84
|
+
- `target.scheduled_date`: from entry Data column (convert to ISO 8601)
|
|
85
|
+
- `target.categories`: from site config defaults
|
|
86
|
+
- `distribution.channels`: from entry Canali column (parse comma-separated)
|
|
87
|
+
- `content.title`: from entry Titolo column
|
|
88
|
+
- `status: draft` (brief starts as draft; user fills content)
|
|
89
|
+
d. Update the calendar entry: `status: planned` becomes `draft`, set the Brief ID column
|
|
90
|
+
|
|
91
|
+
3. For entries with an existing Brief ID (already has a brief): skip creation, just verify the brief file exists in `pipeline-active/`. If the file is missing, report it.
|
|
92
|
+
|
|
93
|
+
4. Write the updated calendar back to the `.state.md` file.
|
|
94
|
+
|
|
95
|
+
5. Report to user:
|
|
96
|
+
```
|
|
97
|
+
N brief creati in pipeline-active/. Compila il contenuto e imposta status: ready.
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
**Safety rules:**
|
|
101
|
+
- NEVER create briefs for entries with `[da assegnare]` title -- report them as needing titles first
|
|
102
|
+
- NEVER overwrite existing brief files -- if a Brief ID already exists as a file, skip and report
|
|
103
|
+
- Show a summary of briefs to be created and ask user confirmation before writing any files
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## Step 3: SCHEDULE -- Convert ready briefs to WordPress scheduled posts
|
|
108
|
+
|
|
109
|
+
**When to run:** User asks to "schedule ready posts" or "schedula i post pronti".
|
|
110
|
+
|
|
111
|
+
**Procedure:**
|
|
112
|
+
|
|
113
|
+
1. Read the current editorial calendar `.state.md` file.
|
|
114
|
+
|
|
115
|
+
2. For each entry with `status: ready`:
|
|
116
|
+
a. Read the corresponding brief file from `pipeline-active/{brief_id}.brief.md`
|
|
117
|
+
b. Verify the brief has `status: ready` (consistency check between calendar and brief)
|
|
118
|
+
c. Create the WordPress post using MCP tool:
|
|
119
|
+
```
|
|
120
|
+
create_content:
|
|
121
|
+
content_type: {entry.Tipo}
|
|
122
|
+
title: {brief.content.title}
|
|
123
|
+
content: {brief body markdown}
|
|
124
|
+
excerpt: {brief.content.excerpt}
|
|
125
|
+
status: "future"
|
|
126
|
+
date: {entry.Data as ISO 8601 datetime with site config publish_time}
|
|
127
|
+
slug: {auto-generated from title}
|
|
128
|
+
```
|
|
129
|
+
d. Assign taxonomy terms using MCP tool:
|
|
130
|
+
```
|
|
131
|
+
assign_terms_to_content:
|
|
132
|
+
content_type: {entry.Tipo}
|
|
133
|
+
id: {post_id}
|
|
134
|
+
categories: {brief.target.categories}
|
|
135
|
+
tags: {brief.target.tags}
|
|
136
|
+
```
|
|
137
|
+
e. Update the calendar entry: `status: ready` becomes `scheduled`, set Post ID column
|
|
138
|
+
f. Update the brief file: add `post_id` and `post_url` to frontmatter
|
|
139
|
+
|
|
140
|
+
3. If the entry has `distribution.channels` (from the Canali column):
|
|
141
|
+
- Compute `scheduled_at` using the entry date + `config.cadence.publish_time` + `distribution.schedule_offset_hours`
|
|
142
|
+
- For Buffer: call `buf_create_update` with the computed `scheduled_at`
|
|
143
|
+
```
|
|
144
|
+
buf_create_update:
|
|
145
|
+
profile_ids: [{config.channels.buffer.profile_id}]
|
|
146
|
+
text: {adapted content}
|
|
147
|
+
media:
|
|
148
|
+
photo: {featured image URL if available}
|
|
149
|
+
scheduled_at: {ISO 8601 UTC timestamp}
|
|
150
|
+
```
|
|
151
|
+
- For Mailchimp: note for manual campaign creation (not auto-scheduled at this step)
|
|
152
|
+
|
|
153
|
+
4. Write the updated calendar back to the `.state.md` file.
|
|
154
|
+
|
|
155
|
+
5. Report to user:
|
|
156
|
+
```
|
|
157
|
+
N post schedulati su WordPress. Verranno pubblicati automaticamente alla data prevista.
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
**Safety rules:**
|
|
161
|
+
- ALWAYS create WordPress posts as `status: future` (never directly `publish`)
|
|
162
|
+
- ALWAYS confirm with user before scheduling, showing the list of posts and their dates
|
|
163
|
+
- If `create_content` fails for any entry, stop processing that entry, report the error, and keep the entry as `ready`
|
|
164
|
+
- Verify the scheduled date is in the future before calling `create_content` -- if the date is in the past, report to user and skip
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## Step 4: SYNC -- Synchronize WordPress status back to calendar
|
|
169
|
+
|
|
170
|
+
**When to run:** User asks to "sync calendar" or "aggiorna stato calendario".
|
|
171
|
+
|
|
172
|
+
**Procedure:**
|
|
173
|
+
|
|
174
|
+
1. Read the current editorial calendar `.state.md` file.
|
|
175
|
+
|
|
176
|
+
2. For each entry with a Post ID (status: `scheduled` or `published`):
|
|
177
|
+
a. Call `list_content` with the content type and filter by post ID:
|
|
178
|
+
```
|
|
179
|
+
list_content:
|
|
180
|
+
content_type: {entry.Tipo}
|
|
181
|
+
search: {post_id}
|
|
182
|
+
per_page: 1
|
|
183
|
+
```
|
|
184
|
+
b. Check the returned post status and update the calendar accordingly:
|
|
185
|
+
- If WP status = `publish` and calendar status = `scheduled` --> update to `published`
|
|
186
|
+
- If WP status = `future` --> keep as `scheduled`
|
|
187
|
+
- If WP status = `draft` --> note: post was reverted, update calendar to `draft`
|
|
188
|
+
- If WP status = `trash` --> note: post was deleted, report to user
|
|
189
|
+
|
|
190
|
+
3. Recalculate `goals.posts_published` = count of entries with `status: published`.
|
|
191
|
+
|
|
192
|
+
4. Write the updated calendar back to the `.state.md` file.
|
|
193
|
+
|
|
194
|
+
5. Report to user:
|
|
195
|
+
```
|
|
196
|
+
Sync calendario {calendar_id}:
|
|
197
|
+
- Post pubblicati: {posts_published}/{posts_target}
|
|
198
|
+
- Post schedulati: {scheduled_count}
|
|
199
|
+
- Post in lavorazione: {draft_count + ready_count}
|
|
200
|
+
- Slot da assegnare: {planned_count}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
**Safety rules:**
|
|
204
|
+
- NEVER modify WordPress post status during sync -- sync is read-only from WordPress
|
|
205
|
+
- If a post was deleted or trashed, report to user but do not remove the calendar entry (mark with a note instead)
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
## Creating a Calendar Manually
|
|
210
|
+
|
|
211
|
+
When the user wants to create a calendar without running the full PLAN step:
|
|
212
|
+
|
|
213
|
+
1. Copy the structure from `references/editorial-schema.md`
|
|
214
|
+
2. Fill in `site_id`, dates for each entry, and known titles
|
|
215
|
+
3. Set `status: planned` for all entries
|
|
216
|
+
4. Save as `.content-state/{YYYY-MM}-editorial.state.md`
|
|
217
|
+
5. Then run the BRIEF step to generate brief files from the planned entries
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## Safety Rules Summary
|
|
222
|
+
|
|
223
|
+
- **ALWAYS** show the calendar and briefs to the user before writing any files
|
|
224
|
+
- **ALWAYS** create WordPress posts as `status: future` (never `publish` directly)
|
|
225
|
+
- **NEVER** overwrite existing briefs or calendar entries with higher status
|
|
226
|
+
- **NEVER** modify WordPress posts during SYNC (read-only operation)
|
|
227
|
+
- **ALWAYS** confirm with the user before scheduling posts to WordPress
|
|
228
|
+
- **LOG** all scheduling actions in the calendar state file
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## Known Limitations
|
|
233
|
+
|
|
234
|
+
### AIWU MCP Server (cloud-based)
|
|
235
|
+
|
|
236
|
+
The AIWU MCP server (`mcp__claude_ai_AIWU`) does **not** support the `post_date` field in `wp_create_post` or `wp_update_post`. This means `post_status: "future"` cannot be achieved through AIWU — WordPress auto-publishes the post when no future date is provided.
|
|
237
|
+
|
|
238
|
+
**E2E test results (2026-03-02):**
|
|
239
|
+
- `wp_create_post` with `post_status: "future"` → auto-published (no `post_date` parameter available)
|
|
240
|
+
- `wp_update_post` with `post_date` + `post_status: "future"` from `draft` → auto-published (`post_date` not passed through)
|
|
241
|
+
- `wp_update_post` on already-published post → `post_date` updated but status cannot revert from `publish` to `future`
|
|
242
|
+
|
|
243
|
+
**Workaround:** Use the wp-rest-bridge MCP server's `create_content` tool, which natively supports the `date` parameter for scheduling future posts. AIWU can still be used for SYNC (read-only) and for content management tasks that don't require future scheduling.
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
## Reference Files
|
|
248
|
+
|
|
249
|
+
- `references/editorial-schema.md` -- complete schema for editorial calendar `.state.md` files
|
|
250
|
+
- `../wp-content-pipeline/references/content-brief-schema.md` -- brief file format and frontmatter fields
|
|
251
|
+
- `../wp-content-pipeline/references/site-config-schema.md` -- site configuration defaults and cadence settings
|
|
252
|
+
- `../wp-analytics/references/signals-feed-schema.md` -- signals feed format for topic suggestions
|
|
253
|
+
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
## Related Skills
|
|
257
|
+
|
|
258
|
+
- **`wp-content-pipeline`** -- publishes briefs created by the BRIEF step through the full pipeline workflow
|
|
259
|
+
- **`wp-analytics`** -- generates the signals feed consumed by the PLAN step for data-driven topic ideas
|
|
260
|
+
- **`wp-content`** -- content creation and management (provides `create_content`, `list_content` MCP tools)
|
|
261
|
+
- **`wp-social-email`** -- distribution channel scheduling (Buffer via `buf_create_update`, Mailchimp campaigns)
|
|
262
|
+
- **`wp-content-repurposing`** -- multi-format adaptation for social distribution channels
|