claude-plugin-wordpress-manager 2.3.1 → 2.6.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 (65) hide show
  1. package/.claude-plugin/plugin.json +15 -3
  2. package/CHANGELOG.md +62 -0
  3. package/agents/wp-content-strategist.md +104 -0
  4. package/agents/wp-distribution-manager.md +98 -0
  5. package/docs/GUIDE.md +183 -23
  6. package/docs/plans/2026-03-01-tier3-wcop-design.md +373 -0
  7. package/docs/plans/2026-03-01-tier3-wcop-implementation.md +915 -0
  8. package/hooks/hooks.json +18 -0
  9. package/package.json +18 -3
  10. package/servers/wp-rest-bridge/build/tools/buffer.d.ts +3 -0
  11. package/servers/wp-rest-bridge/build/tools/buffer.js +205 -0
  12. package/servers/wp-rest-bridge/build/tools/comments.d.ts +6 -6
  13. package/servers/wp-rest-bridge/build/tools/gsc.d.ts +3 -0
  14. package/servers/wp-rest-bridge/build/tools/gsc.js +354 -0
  15. package/servers/wp-rest-bridge/build/tools/index.d.ts +38 -38
  16. package/servers/wp-rest-bridge/build/tools/index.js +12 -0
  17. package/servers/wp-rest-bridge/build/tools/mailchimp.d.ts +3 -0
  18. package/servers/wp-rest-bridge/build/tools/mailchimp.js +265 -0
  19. package/servers/wp-rest-bridge/build/tools/media.d.ts +2 -2
  20. package/servers/wp-rest-bridge/build/tools/multisite-sites.d.ts +2 -2
  21. package/servers/wp-rest-bridge/build/tools/plugin-repository.d.ts +1 -1
  22. package/servers/wp-rest-bridge/build/tools/search.d.ts +2 -2
  23. package/servers/wp-rest-bridge/build/tools/sendgrid.d.ts +3 -0
  24. package/servers/wp-rest-bridge/build/tools/sendgrid.js +255 -0
  25. package/servers/wp-rest-bridge/build/tools/unified-content.d.ts +8 -8
  26. package/servers/wp-rest-bridge/build/tools/unified-taxonomies.d.ts +4 -4
  27. package/servers/wp-rest-bridge/build/tools/users.d.ts +6 -6
  28. package/servers/wp-rest-bridge/build/tools/wc-coupons.d.ts +1 -1
  29. package/servers/wp-rest-bridge/build/tools/wc-customers.d.ts +3 -3
  30. package/servers/wp-rest-bridge/build/tools/wc-orders.d.ts +4 -4
  31. package/servers/wp-rest-bridge/build/tools/wc-products.d.ts +8 -8
  32. package/servers/wp-rest-bridge/build/tools/wc-webhooks.d.ts +4 -4
  33. package/servers/wp-rest-bridge/build/types.d.ts +122 -0
  34. package/servers/wp-rest-bridge/build/wordpress.d.ts +14 -0
  35. package/servers/wp-rest-bridge/build/wordpress.js +151 -0
  36. package/servers/wp-rest-bridge/package.json +1 -0
  37. package/skills/wordpress-router/references/decision-tree.md +8 -2
  38. package/skills/wp-content/SKILL.md +2 -0
  39. package/skills/wp-content-attribution/SKILL.md +2 -0
  40. package/skills/wp-content-optimization/SKILL.md +172 -0
  41. package/skills/wp-content-optimization/references/content-freshness.md +234 -0
  42. package/skills/wp-content-optimization/references/headline-optimization.md +171 -0
  43. package/skills/wp-content-optimization/references/meta-optimization.md +243 -0
  44. package/skills/wp-content-optimization/references/readability-analysis.md +201 -0
  45. package/skills/wp-content-optimization/references/seo-content-scoring.md +245 -0
  46. package/skills/wp-content-optimization/scripts/content_optimization_inspect.mjs +237 -0
  47. package/skills/wp-content-repurposing/SKILL.md +1 -0
  48. package/skills/wp-monitoring/SKILL.md +1 -0
  49. package/skills/wp-programmatic-seo/SKILL.md +2 -0
  50. package/skills/wp-search-console/SKILL.md +121 -0
  51. package/skills/wp-search-console/references/competitor-gap-analysis.md +226 -0
  52. package/skills/wp-search-console/references/content-seo-feedback.md +181 -0
  53. package/skills/wp-search-console/references/gsc-setup.md +110 -0
  54. package/skills/wp-search-console/references/indexing-management.md +182 -0
  55. package/skills/wp-search-console/references/keyword-tracking.md +181 -0
  56. package/skills/wp-search-console/scripts/search_console_inspect.mjs +178 -0
  57. package/skills/wp-social-email/SKILL.md +152 -0
  58. package/skills/wp-social-email/references/audience-segmentation.md +173 -0
  59. package/skills/wp-social-email/references/buffer-social-publishing.md +124 -0
  60. package/skills/wp-social-email/references/content-to-distribution.md +156 -0
  61. package/skills/wp-social-email/references/distribution-analytics.md +208 -0
  62. package/skills/wp-social-email/references/mailchimp-integration.md +145 -0
  63. package/skills/wp-social-email/references/sendgrid-transactional.md +165 -0
  64. package/skills/wp-social-email/scripts/distribution_inspect.mjs +165 -0
  65. package/skills/wp-webhooks/SKILL.md +1 -0
@@ -0,0 +1,201 @@
1
+ # Readability Analysis
2
+
3
+ ## Overview
4
+
5
+ Readability analysis measures how easy content is to read and understand. Claude performs Flesch-Kincaid scoring, sentence length analysis, passive voice detection, paragraph assessment, and jargon identification directly on WordPress content — no external readability tools required. The goal is web-optimized content that matches the target audience's reading level.
6
+
7
+ ## Flesch-Kincaid Scoring
8
+
9
+ ### Formula
10
+ ```
11
+ Flesch Reading Ease = 206.835 - (1.015 × ASL) - (84.6 × ASW)
12
+
13
+ Where:
14
+ ASL = Average Sentence Length (words per sentence)
15
+ ASW = Average Syllables per Word
16
+ ```
17
+
18
+ ### Score Interpretation
19
+
20
+ | Score | Grade Level | Audience | Action |
21
+ |-------|-------------|----------|--------|
22
+ | 90-100 | 5th grade | Very easy, children | Too simple for most web content |
23
+ | 80-89 | 6th grade | Easy, conversational | Good for broad consumer content |
24
+ | 70-79 | 7th grade | Fairly easy | Good for general web content |
25
+ | 60-69 | 8th-9th grade | Standard | **Target for most WordPress content** |
26
+ | 50-59 | 10th-12th grade | Fairly difficult | Acceptable for professional audience |
27
+ | 30-49 | College | Difficult | Only for specialized/technical content |
28
+ | 0-29 | Graduate | Very difficult | Rewrite for web consumption |
29
+
30
+ ### Target Scores by Content Type
31
+
32
+ | Content Type | Target Score | Rationale |
33
+ |--------------|-------------|-----------|
34
+ | Blog posts | 60-70 | General audience, scannable |
35
+ | Product descriptions | 65-75 | Clear, benefit-focused |
36
+ | Technical documentation | 45-60 | Professional audience |
37
+ | Landing pages | 70-80 | Must be instantly clear |
38
+ | Email newsletters | 65-75 | Quick reading format |
39
+
40
+ ## Sentence Length Analysis
41
+
42
+ ### Guidelines
43
+
44
+ | Metric | Target | Flag Threshold |
45
+ |--------|--------|----------------|
46
+ | Average sentence length | 15-20 words | >20 words average |
47
+ | Maximum sentence length | 35 words | >40 words |
48
+ | Short sentence ratio | 20-30% under 10 words | <10% short sentences |
49
+ | Variety | Mix of short, medium, long | All same length |
50
+
51
+ ### Sentence Length Distribution
52
+ Ideal content alternates between sentence lengths for rhythm:
53
+ ```
54
+ Short sentence (5-10 words). ← Punchy, creates emphasis
55
+ Medium sentence (11-20 words). ← Core information delivery
56
+ Long sentence (21-30 words). ← Complex ideas, supporting detail
57
+ Short sentence (5-10 words). ← Reset reader attention
58
+ ```
59
+
60
+ ### Fixing Long Sentences
61
+ Strategies for breaking up long sentences:
62
+ 1. **Split at conjunctions** — break at "and", "but", "while", "although"
63
+ 2. **Remove relative clauses** — move "which" and "that" clauses to new sentences
64
+ 3. **Use bullet lists** — convert compound sentences into scannable lists
65
+ 4. **Front-load the point** — put the main idea first, details after
66
+
67
+ ### Example
68
+ ```
69
+ Before (38 words):
70
+ "WordPress is a content management system that allows users to create websites
71
+ and blogs with themes and plugins, which can be customized to match any brand
72
+ identity and extended with additional functionality."
73
+
74
+ After (two sentences, 15 + 16 words):
75
+ "WordPress is a content management system for creating websites and blogs.
76
+ Themes and plugins let you customize the design and extend functionality."
77
+ ```
78
+
79
+ ## Passive Voice Detection
80
+
81
+ ### What to Flag
82
+ Passive voice constructions where the subject receives the action:
83
+ ```
84
+ Passive: "The plugin was installed by the user."
85
+ Active: "The user installed the plugin."
86
+ ```
87
+
88
+ ### Target
89
+ - Maximum 10% of sentences in passive voice
90
+ - 5% or less is ideal for web content
91
+
92
+ ### Common Passive Patterns
93
+ | Passive Pattern | Active Rewrite |
94
+ |----------------|----------------|
95
+ | "was created by" | "[subject] created" |
96
+ | "is recommended" | "we recommend" |
97
+ | "can be configured" | "you can configure" |
98
+ | "has been updated" | "we updated" / "[subject] updated" |
99
+ | "should be installed" | "install" (imperative) |
100
+
101
+ ### When Passive Is Acceptable
102
+ - Scientific or technical writing where the actor is irrelevant
103
+ - When the object is more important than the subject
104
+ - Policy or legal statements
105
+
106
+ ## Paragraph Length Guidelines
107
+
108
+ | Metric | Target | Flag |
109
+ |--------|--------|------|
110
+ | Sentences per paragraph | 2-4 | >5 sentences |
111
+ | Words per paragraph | 40-80 | >100 words |
112
+ | One-sentence paragraphs | Occasional for emphasis | >3 consecutive |
113
+ | Wall of text | Never | Any paragraph >150 words |
114
+
115
+ ### Web-Specific Rules
116
+ - Break up paragraphs more aggressively than print
117
+ - Use subheadings every 200-300 words
118
+ - Include visual breaks (images, lists, blockquotes) every 300-400 words
119
+ - One idea per paragraph
120
+
121
+ ## Jargon and Complexity Detection
122
+
123
+ ### Flag These Patterns
124
+ 1. **Industry jargon** — terms unknown to general audience (e.g., "canonical URL", "transclusion")
125
+ 2. **Acronyms without definition** — first use must define the acronym
126
+ 3. **Multi-syllable alternatives** — when simpler words exist
127
+ 4. **Nominalization** — turning verbs into nouns ("optimization" instead of "optimize")
128
+ 5. **Double negatives** — "not uncommon" instead of "common"
129
+
130
+ ### Simplification Table
131
+
132
+ | Complex | Simple |
133
+ |---------|--------|
134
+ | utilize | use |
135
+ | implement | add / set up |
136
+ | functionality | feature |
137
+ | methodology | method |
138
+ | in order to | to |
139
+ | at this point in time | now |
140
+ | a large number of | many |
141
+ | in the event that | if |
142
+ | prior to | before |
143
+ | subsequent to | after |
144
+
145
+ ## Step-by-Step Workflow
146
+
147
+ ### Step 1: Fetch Content
148
+ ```
149
+ Use get_content with the post ID to retrieve full body content.
150
+ Strip HTML tags for text-only analysis.
151
+ ```
152
+
153
+ ### Step 2: Compute Metrics
154
+ Claude analyzes the plain text and computes:
155
+ - Total word count
156
+ - Total sentence count
157
+ - Average sentence length (words/sentences)
158
+ - Estimated Flesch-Kincaid score
159
+ - Passive voice percentage
160
+ - Average paragraph length
161
+ - Jargon terms identified
162
+
163
+ ### Step 3: Generate Report
164
+ ```
165
+ Readability Report for: "Post Title"
166
+ ═══════════════════════════════════
167
+ Flesch-Kincaid Score: 58 (target: 60-70) ⚠ Below target
168
+ Average Sentence Length: 22 words (target: <20) ⚠ Too long
169
+ Passive Voice: 15% (target: <10%) ⚠ Too high
170
+ Avg Paragraph Length: 95 words (target: <80) ⚠ Long paragraphs
171
+ Jargon Terms Found: 3 (canonical, transclusion, REST endpoint)
172
+
173
+ Top Issues:
174
+ 1. 5 sentences over 35 words — break into shorter sentences
175
+ 2. 3 paragraphs over 100 words — split with subheadings
176
+ 3. "canonical URL" used without definition — add brief explanation
177
+ ```
178
+
179
+ ### Step 4: Suggest Improvements
180
+ Claude generates specific rewrite suggestions:
181
+ - Rewrites for the 3 longest sentences
182
+ - Paragraph split points with suggested subheadings
183
+ - Passive-to-active voice conversions
184
+ - Jargon simplifications or definitions to add
185
+
186
+ ### Step 5: Apply Changes (Optional)
187
+ ```
188
+ Use update_content to apply approved changes.
189
+ Preserve all HTML structure, links, and media.
190
+ Only modify text content for readability improvements.
191
+ ```
192
+
193
+ ## Best Practices
194
+
195
+ - Always analyze the full post body, not just excerpts
196
+ - Consider the target audience when evaluating scores (technical docs have lower target)
197
+ - Preserve the author's voice — readability improvements should not flatten style
198
+ - Do not oversimplify to the point of losing nuance or accuracy
199
+ - Run readability analysis after SEO optimization to ensure keyword insertion did not harm readability
200
+ - Check readability on both desktop and mobile (shorter paragraphs matter more on mobile)
201
+ - Use transition words (however, therefore, for example) to maintain flow after sentence splitting
@@ -0,0 +1,245 @@
1
+ # SEO Content Scoring
2
+
3
+ ## Overview
4
+
5
+ SEO content scoring evaluates how well a piece of WordPress content is optimized for search engines. Claude performs keyword density analysis, heading hierarchy assessment, internal and external linking checks, image alt text coverage, and content structure evaluation. When GSC data is available, Claude cross-references actual search queries to validate keyword targeting.
6
+
7
+ ## Keyword Density Analysis
8
+
9
+ ### Target Ranges
10
+
11
+ | Keyword Type | Target Density | Minimum | Maximum |
12
+ |-------------|---------------|---------|---------|
13
+ | Primary keyword | 1-2% | 0.5% | 3% |
14
+ | Secondary keywords | 0.5-1% each | 0.3% | 1.5% |
15
+ | LSI/related terms | Natural occurrence | — | — |
16
+
17
+ ### Density Calculation
18
+ ```
19
+ Keyword Density = (Number of keyword occurrences / Total word count) × 100
20
+ ```
21
+
22
+ ### Placement Rules
23
+ 1. **Title (H1)** — primary keyword must appear (preferably front-loaded)
24
+ 2. **First paragraph** — primary keyword within first 100 words
25
+ 3. **Subheadings** — primary or secondary keyword in at least 1 H2
26
+ 4. **Body distribution** — keyword spread throughout, not clustered
27
+ 5. **Last paragraph** — primary keyword in conclusion
28
+ 6. **URL slug** — primary keyword in the URL
29
+
30
+ ### Keyword Stuffing Detection
31
+ Flag when:
32
+ - Primary keyword density exceeds 3%
33
+ - Same keyword appears in consecutive sentences
34
+ - Keyword is forced into unnatural phrasing
35
+ - Multiple exact-match keyword repetitions in one paragraph
36
+
37
+ ### Example Analysis
38
+ ```
39
+ Post: "WordPress Speed Optimization Guide" (1500 words)
40
+ Primary keyword: "wordpress speed optimization"
41
+
42
+ Occurrences: 18 times
43
+ Density: 18/1500 × 100 = 1.2% ✓ Within target
44
+
45
+ Distribution:
46
+ - Title: ✓ Present
47
+ - First paragraph: ✓ Present (word 12)
48
+ - H2 headings: ✓ 1 of 4 H2s contains keyword
49
+ - Last paragraph: ✓ Present
50
+ - URL: ✓ /wordpress-speed-optimization-guide
51
+ ```
52
+
53
+ ## H2/H3 Hierarchy Coverage
54
+
55
+ ### Structure Requirements
56
+
57
+ | Element | Requirement | Check |
58
+ |---------|------------|-------|
59
+ | H1 | Exactly 1 per page, contains primary keyword | Required |
60
+ | H2 | 2-6 per post, secondary keywords in 50%+ | Required |
61
+ | H3 | As needed under H2, long-tail keywords | Recommended |
62
+ | H4+ | Rare, deep detail only | Optional |
63
+ | Skip levels | Never (H1 → H3 without H2) | Flag as error |
64
+
65
+ ### Heading Optimization Checklist
66
+ - [ ] One H1 only, matching the post title
67
+ - [ ] H2s outline the main sections (scannable table of contents)
68
+ - [ ] At least one H2 contains the primary keyword
69
+ - [ ] H3s provide detail under their parent H2
70
+ - [ ] No heading level is skipped
71
+ - [ ] Headings are descriptive (not generic like "More Info")
72
+ - [ ] Secondary keywords distributed across H2/H3 headings
73
+
74
+ ### Example Heading Audit
75
+ ```
76
+ H1: WordPress Speed Optimization: Complete Guide ✓ Primary keyword
77
+ H2: Why WordPress Speed Matters ✓ Descriptive
78
+ H3: Impact on SEO Rankings ✓ Secondary keyword
79
+ H3: Impact on User Experience ✓ Related term
80
+ H2: How to Measure WordPress Speed ✓ Secondary keyword
81
+ H2: 10 WordPress Speed Optimization Techniques ✓ Primary keyword
82
+ H3: Enable Caching ✓ Specific
83
+ H3: Optimize Images ✓ Specific
84
+ H3: Minimize CSS and JavaScript ✓ Specific
85
+ H2: Conclusion ⚠ Generic, add keyword
86
+ ```
87
+
88
+ ## Internal Linking Analysis
89
+
90
+ ### Minimum Standards
91
+
92
+ | Content Length | Internal Links | Rationale |
93
+ |---------------|---------------|-----------|
94
+ | < 500 words | 1-2 links | Short content, few linking opportunities |
95
+ | 500-1000 words | 2-3 links | Standard blog post |
96
+ | 1000-2000 words | 3-5 links | In-depth article |
97
+ | 2000+ words | 5-8 links | Pillar content, extensive topic |
98
+
99
+ ### What to Check
100
+ 1. **Link count** — meets minimum for content length
101
+ 2. **Anchor text** — descriptive, keyword-rich (not "click here")
102
+ 3. **Link relevance** — linked pages are topically related
103
+ 4. **Link distribution** — links spread throughout content, not clustered
104
+ 5. **Orphan detection** — identify posts with zero inbound internal links
105
+ 6. **Reciprocal links** — pillar pages link to cluster posts and vice versa
106
+
107
+ ### Link Opportunity Detection
108
+ Claude identifies linking opportunities by:
109
+ - Finding mentions of topics that have dedicated pages on the site
110
+ - Detecting keyword phrases that match other post titles
111
+ - Identifying related content that could provide context
112
+ - Suggesting links to pillar pages from cluster content
113
+
114
+ ## External Linking
115
+
116
+ ### Guidelines
117
+
118
+ | Metric | Target |
119
+ |--------|--------|
120
+ | External links per post | 1-2 minimum |
121
+ | Link targets | Authoritative, relevant sources (.gov, .edu, industry leaders) |
122
+ | Link freshness | Source content should be current (not outdated) |
123
+ | Nofollow | Use for sponsored or untrusted links |
124
+ | Open in new tab | Yes for external links (target="_blank") |
125
+
126
+ ### What to Avoid
127
+ - Linking to direct competitors' commercial pages
128
+ - Linking to low-authority or spammy sites
129
+ - Excessive external links (>5 per 1000 words)
130
+ - Broken external links (check periodically)
131
+
132
+ ## Image Alt Text Coverage
133
+
134
+ ### Requirements
135
+ | Check | Standard |
136
+ |-------|----------|
137
+ | All images have alt text | 100% coverage required |
138
+ | Alt text includes keyword | At least 1 image per post |
139
+ | Alt text is descriptive | Describes the image content |
140
+ | Alt text length | 5-15 words |
141
+ | Decorative images | Empty alt="" (not missing alt) |
142
+
143
+ ### Example
144
+ ```
145
+ Good: alt="WordPress performance optimization dashboard showing page load times"
146
+ Bad: alt="image1" or alt="" (on informational image) or alt missing
147
+ ```
148
+
149
+ ## Combining with GSC Data
150
+
151
+ When Google Search Console is available, enrich the SEO scoring with real search data:
152
+
153
+ ### Step 1: Fetch Search Queries for the Page
154
+ ```
155
+ Use gsc_search_analytics with the page URL to get actual queries driving traffic.
156
+ ```
157
+
158
+ ### Step 2: Compare Target vs Actual Keywords
159
+ ```
160
+ Target keyword: "wordpress speed optimization"
161
+ Actual top queries from GSC:
162
+ 1. "wordpress speed" (pos 8, 500 impressions)
163
+ 2. "wordpress performance" (pos 12, 300 impressions)
164
+ 3. "how to speed up wordpress" (pos 15, 200 impressions)
165
+
166
+ Analysis: Content ranks for related terms but not the exact target.
167
+ Action: Strengthen primary keyword presence, add "how to speed up wordpress" as H2.
168
+ ```
169
+
170
+ ### Step 3: Identify Keyword Gaps
171
+ - Queries with high impressions but low CTR → meta description issue
172
+ - Queries with high position (>10) → content depth issue
173
+ - Queries the page ranks for that are not in the content → add sections
174
+
175
+ ## SEO Content Score Card
176
+
177
+ ### Scoring Template
178
+ ```
179
+ SEO Content Score for: "Post Title"
180
+ ═════════════════════════════════════
181
+ Keyword Density: 1.2% ✓ (target: 1-2%)
182
+ Keyword in H1: Yes ✓
183
+ Keyword in First 100: Yes ✓
184
+ Keyword in URL: Yes ✓
185
+
186
+ H2 Count: 4 ✓ (target: 2-6)
187
+ H2 with Keywords: 2/4 ✓ (target: 50%+)
188
+ H3 Count: 6 ✓
189
+ Heading Hierarchy: Valid ✓ (no skipped levels)
190
+
191
+ Internal Links: 2 ⚠ (target: 3-5 for 1200 words)
192
+ External Links: 1 ✓ (target: 1-2)
193
+ Anchor Text Quality: Good ✓
194
+
195
+ Image Alt Coverage: 3/4 ⚠ (1 image missing alt text)
196
+ Keyword in Alt: 1/4 ✓
197
+
198
+ Overall Score: 7.5/10
199
+ Priority Fixes:
200
+ 1. Add 1-2 more internal links to related content
201
+ 2. Add alt text to image in section 3
202
+ ```
203
+
204
+ ## Step-by-Step Workflow
205
+
206
+ ### Step 1: Fetch Content
207
+ ```
208
+ Use get_content to retrieve the full post HTML.
209
+ Parse headings, links, images, and body text.
210
+ ```
211
+
212
+ ### Step 2: Identify Target Keyword
213
+ Either provided by user or extracted from:
214
+ - Post title analysis
215
+ - GSC top query for the URL
216
+ - Content theme analysis
217
+
218
+ ### Step 3: Run All Checks
219
+ Claude analyzes in sequence:
220
+ 1. Keyword density and placement
221
+ 2. Heading hierarchy and keyword coverage
222
+ 3. Internal and external link audit
223
+ 4. Image alt text coverage
224
+ 5. GSC data cross-reference (if available)
225
+
226
+ ### Step 4: Generate Score Card
227
+ Produce the formatted score card with pass/fail for each criterion.
228
+
229
+ ### Step 5: Prioritize Fixes
230
+ Rank issues by impact:
231
+ 1. Missing keyword in title/H1 (highest impact)
232
+ 2. Keyword density out of range
233
+ 3. No internal links
234
+ 4. Missing alt text
235
+ 5. Heading hierarchy issues
236
+ 6. External link additions
237
+
238
+ ## Best Practices
239
+
240
+ - Run SEO scoring before publishing (pre-publish checklist)
241
+ - Re-score after content updates to ensure changes did not break optimization
242
+ - Use GSC data to validate keyword targeting against real search behavior
243
+ - Do not sacrifice readability for SEO score — readability comes first
244
+ - Update scoring analysis quarterly as search patterns evolve
245
+ - Track score changes over time to measure optimization impact
@@ -0,0 +1,237 @@
1
+ /**
2
+ * content_optimization_inspect.mjs — Detect content optimization readiness.
3
+ *
4
+ * Checks WordPress content volume, content age distribution, SEO plugins,
5
+ * readability plugins, GSC availability, and WooCommerce presence.
6
+ *
7
+ * Usage:
8
+ * node content_optimization_inspect.mjs [--cwd=/path/to/project]
9
+ *
10
+ * Exit codes:
11
+ * 0 — content optimization readiness found
12
+ * 1 — no content optimization readiness found
13
+ */
14
+
15
+ import { readFileSync, existsSync, readdirSync } from 'node:fs';
16
+ import { join, resolve } from 'node:path';
17
+ import { argv, stdout, exit } from 'node:process';
18
+
19
+ // ---------------------------------------------------------------------------
20
+ // Helpers
21
+ // ---------------------------------------------------------------------------
22
+
23
+ function readFileSafe(filePath) {
24
+ try { return readFileSync(filePath, 'utf-8'); } catch { return null; }
25
+ }
26
+
27
+ function existsSafe(filePath) {
28
+ try { return existsSync(filePath); } catch { return false; }
29
+ }
30
+
31
+ function globDir(dirPath) {
32
+ try { return readdirSync(dirPath); } catch { return []; }
33
+ }
34
+
35
+ // ---------------------------------------------------------------------------
36
+ // Detectors
37
+ // ---------------------------------------------------------------------------
38
+
39
+ function detectContent(cwd) {
40
+ const indicators = [];
41
+
42
+ // Check wp-content directory exists
43
+ if (!existsSafe(join(cwd, 'wp-content'))) {
44
+ return { has_content: false, indicators };
45
+ }
46
+
47
+ indicators.push('wp-content directory exists');
48
+
49
+ // Check for uploads (indicator of content)
50
+ const uploads = globDir(join(cwd, 'wp-content', 'uploads'));
51
+ if (uploads.length > 0) {
52
+ indicators.push(`wp-content/uploads contains ${uploads.length} entries`);
53
+ }
54
+
55
+ // Check for themes presence
56
+ const themes = globDir(join(cwd, 'wp-content', 'themes'));
57
+ if (themes.length > 0) {
58
+ indicators.push(`${themes.length} theme(s) installed`);
59
+ }
60
+
61
+ const hasContent = uploads.length > 0 || themes.length > 0;
62
+ return { has_content: hasContent, indicators };
63
+ }
64
+
65
+ function detectContentAge(cwd) {
66
+ const indicators = [];
67
+ const yearsFound = [];
68
+
69
+ const uploadsPath = join(cwd, 'wp-content', 'uploads');
70
+ if (!existsSafe(uploadsPath)) {
71
+ return { years_found: yearsFound, indicators };
72
+ }
73
+
74
+ const entries = globDir(uploadsPath);
75
+ const yearPattern = /^(20\d{2})$/;
76
+
77
+ for (const entry of entries) {
78
+ const match = entry.match(yearPattern);
79
+ if (match) {
80
+ yearsFound.push(parseInt(match[1], 10));
81
+ indicators.push(`upload directory found for year ${match[1]}`);
82
+ }
83
+ }
84
+
85
+ yearsFound.sort();
86
+
87
+ if (yearsFound.length > 1) {
88
+ indicators.push(`content spans ${yearsFound.length} years (${yearsFound[0]}–${yearsFound[yearsFound.length - 1]})`);
89
+ }
90
+
91
+ return { years_found: yearsFound, indicators };
92
+ }
93
+
94
+ function detectSeoPlugins(cwd) {
95
+ const indicators = [];
96
+ const plugins = globDir(join(cwd, 'wp-content', 'plugins'));
97
+
98
+ const seoPlugins = [
99
+ { dir: 'wordpress-seo', name: 'Yoast SEO' },
100
+ { dir: 'seo-by-rank-math', name: 'RankMath' },
101
+ { dir: 'all-in-one-seo-pack', name: 'AIOSEO' },
102
+ ];
103
+
104
+ for (const plugin of plugins) {
105
+ const lower = plugin.toLowerCase();
106
+ for (const seo of seoPlugins) {
107
+ if (lower === seo.dir) {
108
+ indicators.push(`seo_plugin: ${seo.name} (${plugin})`);
109
+ }
110
+ }
111
+ }
112
+
113
+ return { found: indicators.length > 0, indicators };
114
+ }
115
+
116
+ function detectReadability(cwd) {
117
+ const indicators = [];
118
+ const plugins = globDir(join(cwd, 'wp-content', 'plugins'));
119
+
120
+ // Yoast and RankMath both include readability analysis
121
+ const readabilityPlugins = [
122
+ { dir: 'wordpress-seo', name: 'Yoast SEO (readability analysis)' },
123
+ { dir: 'seo-by-rank-math', name: 'RankMath (readability analysis)' },
124
+ ];
125
+
126
+ for (const plugin of plugins) {
127
+ const lower = plugin.toLowerCase();
128
+ for (const rp of readabilityPlugins) {
129
+ if (lower === rp.dir) {
130
+ indicators.push(`readability_plugin: ${rp.name}`);
131
+ }
132
+ }
133
+ }
134
+
135
+ return { found: indicators.length > 0, indicators };
136
+ }
137
+
138
+ function detectGsc() {
139
+ const indicators = [];
140
+ let configured = false;
141
+
142
+ const raw = process.env.WP_SITES_CONFIG;
143
+ if (!raw) return { configured, indicators };
144
+
145
+ let sites;
146
+ try { sites = JSON.parse(raw); } catch { return { configured, indicators }; }
147
+ if (!Array.isArray(sites)) return { configured, indicators };
148
+
149
+ for (const site of sites) {
150
+ const label = site.name || site.url || 'unknown';
151
+ if (site.gsc_service_account_key) {
152
+ configured = true;
153
+ indicators.push(`gsc_service_account_key configured for ${label}`);
154
+ }
155
+ }
156
+
157
+ return { configured, indicators };
158
+ }
159
+
160
+ function detectWoocommerce(cwd) {
161
+ const indicators = [];
162
+ const plugins = globDir(join(cwd, 'wp-content', 'plugins'));
163
+
164
+ for (const plugin of plugins) {
165
+ if (plugin.toLowerCase() === 'woocommerce') {
166
+ indicators.push(`woocommerce plugin detected (${plugin})`);
167
+ }
168
+ }
169
+
170
+ return { found: indicators.length > 0, indicators };
171
+ }
172
+
173
+ // ---------------------------------------------------------------------------
174
+ // Main
175
+ // ---------------------------------------------------------------------------
176
+
177
+ function main() {
178
+ const cwdArg = argv.find(a => a.startsWith('--cwd='));
179
+ const cwd = cwdArg ? resolve(cwdArg.split('=')[1]) : process.cwd();
180
+
181
+ const content = detectContent(cwd);
182
+ const contentAge = detectContentAge(cwd);
183
+ const seoPlugins = detectSeoPlugins(cwd);
184
+ const readability = detectReadability(cwd);
185
+ const gscAvailable = detectGsc();
186
+ const woocommerce = detectWoocommerce(cwd);
187
+
188
+ const found = content.has_content &&
189
+ (seoPlugins.found || readability.found || gscAvailable.configured);
190
+
191
+ const recommendations = [];
192
+
193
+ if (content.has_content) {
194
+ recommendations.push('Content detected — ready for AI-driven optimization analysis');
195
+ }
196
+ if (seoPlugins.found) {
197
+ recommendations.push('SEO plugin detected — headline and meta description optimization can use plugin data');
198
+ }
199
+ if (readability.found) {
200
+ recommendations.push('Readability analysis available — use wp-content-optimization for Flesch-Kincaid scoring');
201
+ }
202
+ if (gscAvailable.configured) {
203
+ recommendations.push('GSC configured — combine search data with content optimization for data-driven improvements');
204
+ }
205
+ if (woocommerce.found) {
206
+ recommendations.push('WooCommerce detected — prioritize optimization of high-revenue content');
207
+ }
208
+ if (contentAge.years_found.length > 1) {
209
+ recommendations.push('Content spans multiple years — run Content Freshness Audit to identify stale content');
210
+ }
211
+ if (!content.has_content && !seoPlugins.found && !readability.found && !gscAvailable.configured) {
212
+ recommendations.push('No content or optimization tools detected — create content first, then use wp-content-optimization');
213
+ }
214
+ if (content.has_content && seoPlugins.found && gscAvailable.configured) {
215
+ recommendations.push('Full optimization stack ready — use wp-content-optimization for comprehensive content triage');
216
+ }
217
+
218
+ const report = {
219
+ tool: 'content_optimization_inspect',
220
+ version: '1.0.0',
221
+ timestamp: new Date().toISOString(),
222
+ cwd,
223
+ found,
224
+ content,
225
+ content_age: contentAge,
226
+ seo_plugins: seoPlugins,
227
+ readability,
228
+ gsc_available: gscAvailable,
229
+ woocommerce,
230
+ recommendations,
231
+ };
232
+
233
+ stdout.write(JSON.stringify(report, null, 2) + '\n');
234
+ exit(found ? 0 : 1);
235
+ }
236
+
237
+ main();
@@ -94,3 +94,4 @@ See `references/platform-specs.md`
94
94
  - **`wp-content`** — content creation and lifecycle management (source content)
95
95
  - **`wp-headless`** — headless content delivery for multi-channel architectures
96
96
  - **`wp-woocommerce`** — product content for e-commerce repurposing
97
+ - **wp-social-email** — publish repurposed content to social and email channels
@@ -129,3 +129,4 @@ See `references/fleet-monitoring.md`
129
129
  - **`wp-security`** — hardening procedures triggered by monitoring alerts
130
130
  - **`wp-performance`** — optimization actions based on performance trends
131
131
  - **`wp-cicd`** — CI/CD quality gates that complement monitoring (pre-deploy checks)
132
+ - **`wp-search-console`** — add GSC search performance checks to periodic SEO monitoring
@@ -95,3 +95,5 @@ The script checks headless frontend presence, SEO plugins, content volume, custo
95
95
  - `wp-rest-api` — REST API endpoints for content CRUD
96
96
  - `wp-content` — content management fundamentals
97
97
  - `wp-content-repurposing` — transform existing content into new formats
98
+ - `wp-search-console` — monitor performance of generated pages via GSC keyword tracking and indexing status
99
+ - `wp-content-optimization` — optimize programmatic page templates for headlines, readability, and SEO scoring