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.
Files changed (29) hide show
  1. package/.claude-plugin/plugin.json +8 -3
  2. package/CHANGELOG.md +55 -0
  3. package/docs/GUIDE.md +240 -1
  4. package/docs/VALIDATION.md +341 -0
  5. package/docs/plans/2026-03-02-content-framework-architecture.md +612 -0
  6. package/docs/plans/2026-03-02-content-framework-strategic-reflections.md +228 -0
  7. package/docs/plans/2026-03-02-content-intelligence-phase2.md +560 -0
  8. package/docs/plans/2026-03-02-content-pipeline-phase1.md +456 -0
  9. package/docs/plans/2026-03-02-editorial-calendar-phase3.md +490 -0
  10. package/docs/validation/.gitkeep +0 -0
  11. package/docs/validation/dashboard.html +286 -0
  12. package/docs/validation/results.json +1705 -0
  13. package/package.json +12 -3
  14. package/scripts/run-validation.mjs +1132 -0
  15. package/servers/wp-rest-bridge/build/server.js +16 -5
  16. package/servers/wp-rest-bridge/build/tools/index.js +0 -9
  17. package/servers/wp-rest-bridge/build/tools/plugin-repository.js +23 -31
  18. package/servers/wp-rest-bridge/build/tools/schema.js +10 -2
  19. package/servers/wp-rest-bridge/build/tools/unified-content.js +10 -2
  20. package/servers/wp-rest-bridge/build/wordpress.d.ts +0 -3
  21. package/servers/wp-rest-bridge/build/wordpress.js +16 -98
  22. package/servers/wp-rest-bridge/package.json +1 -0
  23. package/skills/wp-analytics/SKILL.md +153 -0
  24. package/skills/wp-analytics/references/signals-feed-schema.md +417 -0
  25. package/skills/wp-content-pipeline/SKILL.md +461 -0
  26. package/skills/wp-content-pipeline/references/content-brief-schema.md +377 -0
  27. package/skills/wp-content-pipeline/references/site-config-schema.md +431 -0
  28. package/skills/wp-editorial-planner/SKILL.md +262 -0
  29. package/skills/wp-editorial-planner/references/editorial-schema.md +268 -0
@@ -0,0 +1,461 @@
1
+ ---
2
+ name: wp-content-pipeline
3
+ description: This skill should be used when the user asks to "publish a brief",
4
+ "process content briefs", "publish from GenCorpComm", "run the content pipeline",
5
+ "create a brief", "list pending briefs", or mentions publishing Gen* output
6
+ to WordPress. Orchestrates the flow from structured brief files through
7
+ WordPress publishing and multi-channel distribution.
8
+ version: 1.0.0
9
+ ---
10
+
11
+ # WordPress Content Pipeline Skill
12
+
13
+ ## Overview
14
+
15
+ The content pipeline orchestrates a structured workflow from content brief files (`.brief.md`) through WordPress publishing and multi-channel distribution. Briefs are authored by Gen* skills (GenCorpComm, GenMarketing, GenSignal, GenBrand) or created manually, then placed in `.content-state/pipeline-active/` for processing. The pipeline reads each brief's frontmatter, merges it with site configuration defaults, validates quality gates, publishes to WordPress via MCP tools, distributes to social and email channels, and archives the completed brief. All state is tracked in the `.content-state/` directory.
16
+
17
+ ## When to Use
18
+
19
+ - User has a content brief to publish to WordPress
20
+ - User ran GenCorpComm (or another Gen* skill) and wants to push output to WordPress
21
+ - User asks to check, list, or review pending briefs
22
+ - User wants to create a new brief manually
23
+ - User asks to distribute a published post to social channels
24
+ - User mentions "content pipeline", "brief pipeline", or "publish from brief"
25
+
26
+ ## Pipeline Workflow
27
+
28
+ ```
29
+ SCAN → CONFIG → VALIDATE → PUBLISH → DISTRIBUTE → UPDATE → ARCHIVE
30
+ ```
31
+
32
+ Each step reads specific files, calls specific tools, and has defined decision points. If any step fails or requires user input, the pipeline stops and reports status -- it never silently skips steps.
33
+
34
+ ---
35
+
36
+ ## Step 1: SCAN
37
+
38
+ **What it does:** Discovers brief files that are ready for processing.
39
+
40
+ **Files read:**
41
+ - `.content-state/pipeline-active/*.brief.md` -- all brief files in the active directory
42
+
43
+ **Procedure:**
44
+ 1. List all `.brief.md` files in `.content-state/pipeline-active/`
45
+ 2. Read the YAML frontmatter of each file
46
+ 3. Filter for briefs with `status: ready`
47
+ 4. Present the list to the user with key details for each brief:
48
+ - `brief_id`
49
+ - `content.title`
50
+ - `target.site_id`
51
+ - `target.content_type`
52
+ - `distribution.channels`
53
+ - `created` date
54
+
55
+ **Decision points:**
56
+ - If no `.brief.md` files exist in `pipeline-active/` → report "No briefs found" and stop
57
+ - If files exist but none have `status: ready` → list all briefs with their current status (`draft`, `published`, etc.) and stop
58
+ - If multiple briefs are `ready` → list all and ask user which to process (or process all sequentially if user confirms)
59
+
60
+ **Output format:**
61
+ ```
62
+ Pipeline Scan Results:
63
+ 1. BRF-2026-014 — "Acqua di Cactus: La Rivoluzione" → opencactus (post) [linkedin, twitter]
64
+ 2. BRF-2026-015 — "Summer Campaign Launch" → opencactus (page) [mailchimp]
65
+
66
+ Which brief to process? (enter number, or "all")
67
+ ```
68
+
69
+ ---
70
+
71
+ ## Step 2: CONFIG
72
+
73
+ **What it does:** Loads site configuration and merges it with brief values.
74
+
75
+ **Files read:**
76
+ - `.content-state/{site_id}.config.md` -- site configuration for the brief's target site
77
+
78
+ **Procedure:**
79
+ 1. Read `target.site_id` from the selected brief (e.g., `opencactus`)
80
+ 2. Load `.content-state/{site_id}.config.md` (e.g., `.content-state/opencactus.config.md`)
81
+ 3. Parse the YAML frontmatter for site defaults, brand context, channel config, and SEO settings
82
+ 4. Apply the override hierarchy -- brief values take precedence over site config defaults:
83
+
84
+ ```
85
+ Brief value > Site config default > System default
86
+ ```
87
+
88
+ **Specific merge rules:**
89
+ | Field | Brief source | Config fallback |
90
+ |-------|-------------|-----------------|
91
+ | `content_type` | `brief.target.content_type` | `config.defaults.content_type` |
92
+ | `status` | `brief.target.status` | `config.defaults.status` |
93
+ | `categories` | `brief.target.categories` | `config.defaults.categories` |
94
+ | `author` | `brief.content.author` | `config.defaults.author` |
95
+ | `schema_type` | `brief.seo.schema_type` | `config.seo.default_schema` |
96
+ | `seo_score_min` | `brief.gates.seo_score_min` | `config.seo.min_score` |
97
+
98
+ 5. Read the config body section (Markdown after frontmatter) for brand context -- this informs content adaptation in the DISTRIBUTE step
99
+
100
+ **Decision points:**
101
+ - If config file does not exist for the `site_id` → stop and report: "No site config found for '{site_id}'. Create `.content-state/{site_id}.config.md` first. See `references/site-config-schema.md` for the format."
102
+ - If config is missing required fields (`site_id`, `site_url`, `brand.tone`, `brand.language`) → stop and report which fields are missing
103
+
104
+ ---
105
+
106
+ ## Step 3: VALIDATE
107
+
108
+ **What it does:** Checks quality gates before publishing.
109
+
110
+ Note: Even though the brief has `status: ready`, re-evaluate gates at publish time as a safety check -- briefs may be manually promoted without automated gate validation.
111
+
112
+ **Procedure:**
113
+ 1. Verify all required brief fields are present:
114
+ - `brief_id`, `status`, `source.skill`, `target.site_id`, `target.content_type`, `content.title`
115
+ 2. Verify the Markdown body (after frontmatter) is non-empty
116
+ 3. Check quality gates from the merged configuration:
117
+
118
+ **Gate: `require_review`**
119
+ - If `gates.require_review: true` → present the full brief summary to the user and wait for explicit approval before proceeding
120
+ - Display: title, excerpt, target site, content type, categories, tags, distribution channels, and a preview of the first 500 characters of the body
121
+ - User must confirm with "approve" or "yes" to continue
122
+
123
+ **Gate: SEO and Readability**
124
+ - If `gates.seo_score_min` or `gates.readability_min` are set → evaluate the content quality based on:
125
+ - Keyword usage and placement (title, first paragraph, headings, body density)
126
+ - Meta description length and keyword inclusion
127
+ - Heading structure (single H1, logical H2/H3 hierarchy)
128
+ - Internal link presence (from `seo.internal_links`)
129
+ - Content length and paragraph structure for readability
130
+ - These are evaluated by Claude based on content quality analysis, not automated scoring tools
131
+ - Report an estimated score with reasoning
132
+
133
+ 4. Validate distribution channels against site config:
134
+ - For each channel in `brief.distribution.channels`, check that `config.channels.{channel}.enabled: true`
135
+ - If a brief requests a disabled channel → warn user and remove that channel from the processing list
136
+
137
+ **Decision points:**
138
+ - If required fields are missing → stop, report which fields are missing, keep brief as `status: ready`
139
+ - If body is empty → stop, report "Brief body is empty -- add content before publishing"
140
+ - If `require_review: true` and user does not approve → stop, keep brief as `status: ready`
141
+ - If SEO/readability gates fail → report scores and reasoning, ask user whether to proceed anyway or edit first
142
+ - If all gates pass → proceed to PUBLISH
143
+
144
+ ---
145
+
146
+ ## Step 4: PUBLISH
147
+
148
+ **What it does:** Creates the content on WordPress using MCP tools.
149
+
150
+ **MCP tools used:**
151
+ - `create_content` -- create the WordPress post/page
152
+ - `assign_terms_to_content` -- set categories and tags
153
+ - `sd_inject` -- inject Schema.org structured data (if applicable)
154
+ - `switch_site` -- switch to the target site (if multi-site)
155
+
156
+ **Procedure:**
157
+
158
+ 1. **Switch site** (if needed):
159
+ ```
160
+ switch_site:
161
+ site_id: {target.site_id}
162
+ ```
163
+ If `switch_site` fails (site_id not found in WP_SITES_CONFIG) → stop, report: "Site `{site_id}` not configured. Add credentials to WP_SITES_CONFIG before publishing."
164
+
165
+ 2. **Check for slug duplicates**: Before creating content, check for existing content with the same slug using `list_content` with a search parameter matching the intended slug. If a match is found, report to the user and ask before creating a duplicate.
166
+
167
+ 3. **Create content as draft first** (safety rule -- always draft first):
168
+ ```
169
+ create_content:
170
+ content_type: {target.content_type} # e.g., "post"
171
+ title: {content.title}
172
+ content: {body from brief markdown}
173
+ excerpt: {content.excerpt}
174
+ status: "draft" # always draft initially
175
+ slug: {auto-generated from title}
176
+ ```
177
+ Record the returned `post_id` and `post_url`.
178
+
179
+ 4. **Assign taxonomy terms**:
180
+ ```
181
+ assign_terms_to_content:
182
+ content_type: {target.content_type}
183
+ id: {post_id}
184
+ categories: {target.categories} # e.g., ["sustainability", "innovation"]
185
+ tags: {target.tags} # e.g., ["cactus-water", "zero-calorie"]
186
+ ```
187
+
188
+ 5. **Inject structured data** (if `seo.schema_type` is defined):
189
+ ```
190
+ sd_inject:
191
+ post_id: {post_id}
192
+ schema_type: {seo.schema_type} # e.g., "Article"
193
+ ```
194
+
195
+ 6. **Present draft to user for final confirmation**:
196
+ - Show: title, post URL (draft), categories, tags, schema type
197
+ - Ask: "Content created as draft. Publish now?"
198
+
199
+ 7. **Update to target status** (after user confirms, or if `target.status` is explicitly `publish` in the brief):
200
+ ```
201
+ update_content:
202
+ content_type: {target.content_type}
203
+ id: {post_id}
204
+ status: {target.status} # "publish", "future", etc.
205
+ scheduled_date: {target.scheduled_date} # only if status is "future"
206
+ ```
207
+
208
+ **Decision points:**
209
+ - If `create_content` fails → stop, report the error, keep brief as `status: ready`
210
+ - If user declines to publish after draft → keep as draft on WordPress, keep brief as `status: ready`, report draft URL
211
+ - If `target.status` is explicitly `publish` in the brief → still create as draft first, then update to publish after showing user the draft URL (unless the brief also has `gates.require_review: false`, in which case proceed automatically)
212
+
213
+ ---
214
+
215
+ ## Step 5: DISTRIBUTE
216
+
217
+ **What it does:** Distributes the published content to external channels.
218
+
219
+ **Prerequisite:** Step 4 must have completed successfully with the content in `publish` status on WordPress.
220
+
221
+ **Procedure:**
222
+
223
+ 1. Check if `distribution.channels` is non-empty. If empty, skip to Step 6.
224
+
225
+ 2. For each channel in `distribution.channels`:
226
+
227
+ a. **Verify channel is enabled** in site config (`config.channels.{channel}.enabled: true`). Skip disabled channels with a warning.
228
+
229
+ b. **Adapt content** if `distribution.adapt_format: true`:
230
+ - Use the `wp-content-repurposing` skill patterns to transform the content for the target channel
231
+ - Read `config.channels.{channel}.format` for the adaptation style
232
+ - Read `config.brand` block for tone and language guidance
233
+
234
+ c. **Respect scheduling**: if `distribution.schedule_offset_hours > 0`, note the delayed publish time for each channel post.
235
+
236
+ 3. **Channel-specific MCP tool calls:**
237
+
238
+ **LinkedIn** (`config.channels.linkedin`):
239
+ ```
240
+ li_create_post:
241
+ profile_id: {config.channels.linkedin.profile_id}
242
+ text: {adapted content for LinkedIn}
243
+ link_url: {published WordPress URL}
244
+ visibility: "PUBLIC"
245
+ ```
246
+
247
+ **Twitter** (`config.channels.twitter`):
248
+ - For short content or `format: concise`:
249
+ ```
250
+ tw_create_tweet:
251
+ text: {adapted content for Twitter}
252
+ url: {published WordPress URL}
253
+ ```
254
+ - For long content or `format: thread`:
255
+ ```
256
+ tw_create_thread:
257
+ tweets: [{thread segments adapted from content}]
258
+ url: {published WordPress URL}
259
+ ```
260
+
261
+ **Buffer** (`config.channels.buffer`):
262
+ Compute `scheduled_at` as: current date at `config.cadence.publish_time` plus `distribution.schedule_offset_hours` hours, expressed as UTC ISO 8601 timestamp (e.g., `2026-03-15T14:00:00Z`).
263
+ ```
264
+ buf_create_update:
265
+ profile_ids: [{config.channels.buffer.profile_id}]
266
+ text: {adapted content}
267
+ media:
268
+ photo: {featured image URL if available}
269
+ scheduled_at: {ISO 8601 UTC timestamp}
270
+ ```
271
+
272
+ **Mailchimp** (`config.channels.mailchimp`):
273
+ ```
274
+ mc_create_campaign:
275
+ type: "regular"
276
+ audience_id: {config.channels.mailchimp.audience_id}
277
+ subject: {content.title}
278
+ from_name: {from site brand}
279
+ ```
280
+ Convert the brief's Markdown body to HTML before passing to `mc_set_campaign_content`. Use standard Markdown-to-HTML rendering.
281
+ ```
282
+ mc_set_campaign_content:
283
+ campaign_id: {returned campaign_id}
284
+ html: {email-formatted content from brief}
285
+
286
+ # STOP — require explicit user confirmation before sending
287
+ mc_send_campaign:
288
+ campaign_id: {campaign_id}
289
+ ```
290
+
291
+ 4. Record the result of each channel distribution (success/failure, post ID or URL, timestamp).
292
+
293
+ **Decision points:**
294
+ - If a channel tool call fails → log the failure, continue with remaining channels, report all failures at the end
295
+ - If Mailchimp is in the channel list → ALWAYS stop and confirm with user before calling `mc_send_campaign`
296
+ - If `schedule_offset_hours > 0` → use scheduling parameters in tool calls rather than immediate posting
297
+
298
+ ---
299
+
300
+ ## Step 6: UPDATE
301
+
302
+ **What it does:** Updates the brief file with publishing results.
303
+
304
+ **Procedure:**
305
+
306
+ 1. Update the brief's YAML frontmatter with:
307
+ ```yaml
308
+ status: published
309
+ published_at: {ISO 8601 timestamp of publication}
310
+ post_id: {WordPress post ID}
311
+ post_url: {published WordPress URL}
312
+ distribution_log:
313
+ - channel: linkedin
314
+ status: success
315
+ url: "https://linkedin.com/feed/update/..."
316
+ timestamp: "2026-03-02T12:00:00Z"
317
+ - channel: twitter
318
+ status: success
319
+ tweet_id: "1234567890"
320
+ timestamp: "2026-03-02T12:00:00Z"
321
+ - channel: mailchimp
322
+ status: sent
323
+ campaign_id: "mc_camp_abc123"
324
+ timestamp: "2026-03-02T14:00:00Z"
325
+ ```
326
+
327
+ 2. Write the updated frontmatter back to the brief file in `pipeline-active/` (before archiving).
328
+
329
+ **Decision points:**
330
+ - If some distribution channels failed → still mark brief as `published` (the WordPress publish succeeded), but log failures in `distribution_log` with `status: failed` and error details
331
+ - If WordPress publish itself failed → do not update status, leave as `ready`
332
+
333
+ ---
334
+
335
+ ## Step 7: ARCHIVE
336
+
337
+ **What it does:** Moves the completed brief from active to archive.
338
+
339
+ **Procedure:**
340
+
341
+ 1. Move the brief file from `.content-state/pipeline-active/` to `.content-state/pipeline-archive/`:
342
+ ```
343
+ .content-state/pipeline-active/BRF-2026-014.brief.md
344
+ → .content-state/pipeline-archive/BRF-2026-014.brief.md
345
+ ```
346
+
347
+ 2. Report the final summary to the user:
348
+ ```
349
+ Pipeline Complete:
350
+ Brief: BRF-2026-014
351
+ Title: "Acqua di Cactus: La Rivoluzione Zero-Calorie dalla Sicilia"
352
+ WordPress: https://opencactus.com/acqua-di-cactus-rivoluzione/ (published)
353
+ Distribution:
354
+ - LinkedIn: posted ✓
355
+ - Twitter: posted ✓
356
+ - Mailchimp: sent ✓
357
+ Archived: .content-state/pipeline-archive/BRF-2026-014.brief.md
358
+ ```
359
+
360
+ **Decision points:**
361
+ - If user wants to keep the brief in `pipeline-active/` for further editing → skip archive, report that brief remains active
362
+ - If brief was only partially processed (e.g., published but distribution pending) → keep in `pipeline-active/` until distribution is complete
363
+
364
+ ---
365
+
366
+ ## Creating a Brief Manually
367
+
368
+ When the user wants to create a brief from scratch (not from a Gen* skill):
369
+
370
+ **Procedure:**
371
+
372
+ 1. **Read site config** for defaults:
373
+ - Load `.content-state/{site_id}.config.md`
374
+ - Extract `defaults` block and `brand` block
375
+
376
+ 2. **Generate `brief_id`**:
377
+ - Format: `BRF-YYYY-NNN` where `YYYY` is the current year and `NNN` is zero-padded sequential
378
+ - Scan existing briefs in both `pipeline-active/` and `pipeline-archive/` to determine the next sequential number
379
+ - Example: if `BRF-2026-014` is the highest existing ID, the next brief is `BRF-2026-015`
380
+
381
+ 3. **Write brief file** to `pipeline-active/`:
382
+ ```
383
+ .content-state/pipeline-active/BRF-2026-015.brief.md
384
+ ```
385
+ With frontmatter populated from site defaults:
386
+ ```yaml
387
+ ---
388
+ brief_id: BRF-2026-015
389
+ created: {current ISO 8601 timestamp}
390
+ status: draft
391
+
392
+ source:
393
+ skill: manual
394
+ domain: general
395
+
396
+ target:
397
+ site_id: {user-specified site}
398
+ content_type: {config.defaults.content_type}
399
+ status: {config.defaults.status}
400
+ categories: {config.defaults.categories}
401
+ tags: []
402
+
403
+ content:
404
+ title: "{user-provided title}"
405
+ excerpt: ""
406
+ author: {config.defaults.author}
407
+
408
+ distribution:
409
+ channels: []
410
+ adapt_format: true
411
+ schedule_offset_hours: 0
412
+
413
+ seo:
414
+ focus_keyword: ""
415
+ schema_type: {config.seo.default_schema}
416
+
417
+ gates:
418
+ seo_score_min: {config.seo.min_score}
419
+ readability_min: 60
420
+ require_review: true
421
+ ---
422
+
423
+ # {Title}
424
+
425
+ {User fills in content here}
426
+ ```
427
+
428
+ 4. **Inform user**:
429
+ - Brief created at `pipeline-active/BRF-2026-015.brief.md` with `status: draft`
430
+ - User should fill in the content body and any missing fields
431
+ - When ready, set `status: ready` to enable pipeline processing
432
+
433
+ ---
434
+
435
+ ## Safety Rules
436
+
437
+ - **ALWAYS** create WordPress content as `draft` first, then update to the target status only after user confirmation -- unless the brief explicitly sets `target.status: publish` AND `gates.require_review: false`
438
+ - **NEVER** publish content without showing the user the brief summary first
439
+ - **ALWAYS** confirm with the user before sending Mailchimp email campaigns (`mc_send_campaign`)
440
+ - **ALWAYS** log all distribution actions in the brief's `distribution_log` field
441
+ - **NEVER** activate a distribution channel that is `enabled: false` in the site config, even if the brief requests it
442
+ - **NEVER** modify the brief body content during publishing -- the pipeline publishes what is in the brief, it does not rewrite
443
+ - **ALWAYS** preserve the original brief file (with updated frontmatter) in the archive for audit trail
444
+ - **CHECK** for existing WordPress content with the same slug before creating, to avoid duplicates
445
+
446
+ ---
447
+
448
+ ## Reference Files
449
+
450
+ - **`references/content-brief-schema.md`** -- complete schema for `.brief.md` files including all frontmatter fields, status lifecycle, and validation rules
451
+ - **`references/site-config-schema.md`** -- complete schema for `.config.md` files including brand, defaults, channels, SEO, and cadence configuration
452
+
453
+ ---
454
+
455
+ ## Related Skills
456
+
457
+ - **`wp-content`** -- content creation and management (creating posts, pages, taxonomy management)
458
+ - **`wp-content-optimization`** -- SEO and readability enhancement (optimizing content before publishing)
459
+ - **`wp-content-repurposing`** -- multi-format adaptation for distribution (transforming content for social/email channels)
460
+ - **`wp-social-email`** -- distribution channel tools (Mailchimp, Buffer, SendGrid MCP tool reference)
461
+ - **`wp-structured-data`** -- Schema.org markup injection (structured data for rich search results)