claude-plugin-wordpress-manager 2.9.0 → 2.12.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 (37) hide show
  1. package/CHANGELOG.md +73 -0
  2. package/agents/wp-content-strategist.md +58 -1
  3. package/agents/wp-distribution-manager.md +39 -6
  4. package/docs/GUIDE.md +145 -14
  5. package/docs/plans/2026-03-01-tier6-7-design.md +246 -0
  6. package/docs/plans/2026-03-01-tier6-7-implementation.md +1629 -0
  7. package/hooks/hooks.json +18 -0
  8. package/package.json +6 -3
  9. package/servers/wp-rest-bridge/build/tools/index.js +9 -0
  10. package/servers/wp-rest-bridge/build/tools/linkedin.js +203 -0
  11. package/servers/wp-rest-bridge/build/tools/schema.js +159 -0
  12. package/servers/wp-rest-bridge/build/tools/twitter.js +183 -0
  13. package/servers/wp-rest-bridge/build/wordpress.js +94 -0
  14. package/skills/wordpress-router/references/decision-tree.md +10 -2
  15. package/skills/wp-content-generation/SKILL.md +128 -0
  16. package/skills/wp-content-generation/references/brief-templates.md +151 -0
  17. package/skills/wp-content-generation/references/generation-workflow.md +132 -0
  18. package/skills/wp-content-generation/references/outline-patterns.md +188 -0
  19. package/skills/wp-content-generation/scripts/content_gen_inspect.mjs +90 -0
  20. package/skills/wp-content-repurposing/SKILL.md +13 -0
  21. package/skills/wp-content-repurposing/references/auto-transform-pipeline.md +128 -0
  22. package/skills/wp-content-repurposing/references/transform-templates.md +304 -0
  23. package/skills/wp-linkedin/SKILL.md +96 -0
  24. package/skills/wp-linkedin/references/linkedin-analytics.md +58 -0
  25. package/skills/wp-linkedin/references/linkedin-posting.md +53 -0
  26. package/skills/wp-linkedin/references/linkedin-setup.md +59 -0
  27. package/skills/wp-linkedin/scripts/linkedin_inspect.mjs +55 -0
  28. package/skills/wp-structured-data/SKILL.md +94 -0
  29. package/skills/wp-structured-data/references/injection-patterns.md +160 -0
  30. package/skills/wp-structured-data/references/schema-types.md +127 -0
  31. package/skills/wp-structured-data/references/validation-guide.md +89 -0
  32. package/skills/wp-structured-data/scripts/schema_inspect.mjs +88 -0
  33. package/skills/wp-twitter/SKILL.md +101 -0
  34. package/skills/wp-twitter/references/twitter-analytics.md +60 -0
  35. package/skills/wp-twitter/references/twitter-posting.md +66 -0
  36. package/skills/wp-twitter/references/twitter-setup.md +62 -0
  37. package/skills/wp-twitter/scripts/twitter_inspect.mjs +58 -0
@@ -0,0 +1,304 @@
1
+ # Transform Templates
2
+
3
+ Ready-to-use templates for converting WordPress content into platform-specific formats.
4
+
5
+ ## Template 1: Blog → Tweet (Single)
6
+
7
+ **Format rules:**
8
+ - Max 280 characters (link counts as ~23 chars)
9
+ - Structure: Hook + key takeaway + link + hashtags
10
+ - Tone: Casual, punchy, curiosity-driven
11
+
12
+ **Template:**
13
+ ```
14
+ {hook_sentence} {key_takeaway}
15
+
16
+ {post_url}
17
+
18
+ {hashtags}
19
+ ```
20
+
21
+ **Character budget:**
22
+ | Element | Max Chars |
23
+ |---------|-----------|
24
+ | Hook + takeaway | ~220 |
25
+ | URL | ~23 |
26
+ | Hashtags (2-3) | ~35 |
27
+
28
+ **Example input:**
29
+ ```
30
+ Title: "5 Benefits of Cactus Water for Summer Hydration"
31
+ Excerpt: "Cactus water from Sicilian prickly pear is packed with electrolytes,
32
+ antioxidants, and has zero calories. Here's why it's the perfect summer drink."
33
+ Tags: ["cactus water", "hydration", "zero calorie"]
34
+ ```
35
+
36
+ **Example output:**
37
+ ```
38
+ Zero calories, packed with electrolytes — cactus water is the summer drink
39
+ you didn't know you needed 🌵
40
+
41
+ https://dolcezero.com/cactus-water-benefits
42
+
43
+ #cactuswater #hydration #zerocalorie
44
+ ```
45
+
46
+ **Hashtag generation logic:**
47
+ 1. Take post tags, lowercase, remove spaces → `#cactuswater`
48
+ 2. Add 1-2 category-based hashtags → `#healthydrinks`
49
+ 3. Cap at 3 hashtags for Twitter (engagement drops with more)
50
+
51
+ ---
52
+
53
+ ## Template 2: Blog → Twitter Thread
54
+
55
+ **Format rules:**
56
+ - Tweet 1: Hook with promise ("Here's what we learned about X" or "N things about X")
57
+ - Middle tweets: One key point per tweet, numbered
58
+ - Last tweet: CTA with link
59
+ - Each tweet max 280 chars
60
+ - Split by H2 sections or key points
61
+
62
+ **Template:**
63
+ ```
64
+ Tweet 1 (hook):
65
+ {numbered_promise_or_question}
66
+
67
+ A thread 🧵👇
68
+
69
+ Tweet 2-N (key points):
70
+ {number}/ {section_heading}
71
+
72
+ {key_point_summary}
73
+
74
+ Tweet N+1 (CTA):
75
+ If you found this useful, check out the full article:
76
+
77
+ {post_url}
78
+
79
+ {hashtags}
80
+ ```
81
+
82
+ **Splitting rules:**
83
+ - If post has H2 headings: 1 tweet per H2 (summarize section in 250 chars)
84
+ - If no H2s: Extract sentences with numbers/stats/claims, 1 per tweet
85
+ - Max thread length: 10 tweets (5-7 optimal for engagement)
86
+ - Each middle tweet should stand alone (valuable without reading the thread)
87
+
88
+ **Example input:**
89
+ ```
90
+ Title: "5 Benefits of Cactus Water for Summer Hydration"
91
+ H2s: ["Rich in Electrolytes", "Zero Calories", "Antioxidant Power",
92
+ "Sustainable Sourcing", "Naturally Refreshing"]
93
+ ```
94
+
95
+ **Example output:**
96
+ ```
97
+ Tweet 1:
98
+ 5 reasons cactus water is the smartest hydration choice this summer
99
+
100
+ A thread 🧵👇
101
+
102
+ Tweet 2:
103
+ 1/ Rich in Electrolytes
104
+
105
+ Prickly pear cactus naturally contains potassium, magnesium, and calcium —
106
+ the same electrolytes you'd find in sports drinks, without the sugar.
107
+
108
+ Tweet 3:
109
+ 2/ Zero Calories
110
+
111
+ Unlike coconut water (45 cal) or fruit juice (110+ cal), cactus water
112
+ delivers hydration at exactly 0 calories per serving.
113
+
114
+ ...
115
+
116
+ Tweet 6:
117
+ Found this useful? Read the full breakdown:
118
+
119
+ https://dolcezero.com/cactus-water-benefits
120
+
121
+ #cactuswater #hydration #zerocalorie
122
+ ```
123
+
124
+ ---
125
+
126
+ ## Template 3: Blog → LinkedIn Post
127
+
128
+ **Format rules:**
129
+ - Max 3,000 characters (optimal: 1,300-1,700)
130
+ - Structure: Hook line → story/insight → key takeaway → CTA → hashtags
131
+ - Tone: Professional, thought-leadership, data-driven
132
+ - Line breaks between paragraphs for readability
133
+ - Link in post body (not first comment for simplicity)
134
+
135
+ **Template:**
136
+ ```
137
+ {hook_line}
138
+
139
+ {context_or_story}
140
+
141
+ {key_insight_with_data}
142
+
143
+ {takeaway_or_opinion}
144
+
145
+ {cta_with_link}
146
+
147
+ {hashtags}
148
+ ```
149
+
150
+ **Example input:**
151
+ ```
152
+ Title: "How We Reduced Sugar Content to Zero Without Losing Taste"
153
+ Excerpt: "Our R&D team spent 18 months developing a proprietary process
154
+ that uses Sicilian prickly pear as a natural sweetness base..."
155
+ Tags: ["food technology", "zero sugar", "innovation"]
156
+ ```
157
+
158
+ **Example output:**
159
+ ```
160
+ We spent 18 months on a single question: can a drink taste sweet with
161
+ literally zero sugar?
162
+
163
+ The answer surprised even our food scientists.
164
+
165
+ Sicilian prickly pear cactus has a naturally sweet flavor profile.
166
+ By extracting and concentrating its essence, we created a beverage base
167
+ that delivers sweetness perception without any added sugars or
168
+ artificial sweeteners.
169
+
170
+ The result: a drink that scored 8.2/10 in blind taste tests against
171
+ traditional sweetened beverages.
172
+
173
+ Key insight: consumers don't want "less sugar." They want full flavor
174
+ with zero compromise. That's what drove our entire product philosophy.
175
+
176
+ Full story on our process → https://dolcezero.com/zero-sugar-innovation
177
+
178
+ #FoodTechnology #ZeroSugar #BeverageInnovation #ProductDevelopment
179
+ ```
180
+
181
+ **Hashtag generation logic:**
182
+ 1. Take post tags → professional case: `#FoodTechnology`
183
+ 2. Add industry hashtags: `#Innovation`, `#ProductDevelopment`
184
+ 3. Cap at 3-5 hashtags (LinkedIn penalizes excessive hashtags)
185
+ 4. Use PascalCase for multi-word hashtags
186
+
187
+ ---
188
+
189
+ ## Template 4: Blog → LinkedIn Article
190
+
191
+ **Format rules:**
192
+ - Full long-form content adaptation (up to 125,000 chars)
193
+ - Preserve HTML structure: H2 → article sections, lists → bullet points
194
+ - Add professional introduction paragraph (not in original post)
195
+ - Add author bio and CTA at the end
196
+ - Strip WordPress-specific shortcodes and embeds
197
+ - Requires user confirmation (safety hook active on `li_create_article`)
198
+
199
+ **Template:**
200
+ ```
201
+ Title: {post_title}
202
+
203
+ {professional_intro_paragraph}
204
+
205
+ {adapted_content_with_html}
206
+
207
+ ---
208
+
209
+ {author_bio_cta}
210
+ ```
211
+
212
+ **HTML adaptation rules:**
213
+ - Keep: `<h2>`, `<h3>`, `<p>`, `<ul>`, `<ol>`, `<li>`, `<strong>`, `<em>`, `<blockquote>`
214
+ - Strip: `<script>`, `<style>`, `<iframe>`, WordPress shortcodes `[shortcode]`
215
+ - Convert: `<img>` with featured image only (LinkedIn renders inline images in articles)
216
+ - Remove: WordPress block comments `<!-- wp:paragraph -->`
217
+
218
+ **When to use article vs feed post:**
219
+ | Criteria | Feed Post | Article |
220
+ |----------|-----------|---------|
221
+ | Word count | < 500 words | > 500 words |
222
+ | Content type | Quick insight, update | Tutorial, case study, analysis |
223
+ | Engagement goal | Likes, comments | Thought leadership, saves |
224
+ | Link included | Yes (drive traffic) | Optional (content is self-contained) |
225
+
226
+ ---
227
+
228
+ ## Template 5: Blog → Email Snippet
229
+
230
+ **Format rules:**
231
+ - Structure: Subject line + preview text + snippet body + CTA button
232
+ - Snippet length: 150-200 words (enough to entice, not enough to satisfy)
233
+ - CTA: "Read the full article" or "Continue reading"
234
+ - For use with Mailchimp (`mc_set_campaign_content`) or SendGrid (`sg_send_email`)
235
+
236
+ **Template:**
237
+ ```
238
+ Subject: {compelling_subject_line}
239
+ Preview: {first_line_preview_text}
240
+
241
+ ---
242
+
243
+ {post_title}
244
+
245
+ {excerpt_or_first_paragraph}
246
+
247
+ {one_key_statistic_or_insight}
248
+
249
+ [Read the full article →]({post_url})
250
+ ```
251
+
252
+ **Subject line formulas:**
253
+ 1. **Number + benefit**: "5 ways cactus water improves your hydration"
254
+ 2. **Question**: "Is cactus water the next coconut water?"
255
+ 3. **How-to**: "How we made zero-calorie taste amazing"
256
+ 4. **Curiosity gap**: "The Sicilian secret behind our water"
257
+
258
+ **Example input:**
259
+ ```
260
+ Title: "5 Benefits of Cactus Water for Summer Hydration"
261
+ Excerpt: "Cactus water from Sicilian prickly pear is packed with electrolytes..."
262
+ URL: "https://dolcezero.com/cactus-water-benefits"
263
+ ```
264
+
265
+ **Example output:**
266
+ ```
267
+ Subject: 5 reasons cactus water beats coconut water this summer
268
+ Preview: Zero calories, natural electrolytes, and a taste you won't believe
269
+
270
+ ---
271
+
272
+ 5 Benefits of Cactus Water for Summer Hydration
273
+
274
+ Cactus water from Sicilian prickly pear is packed with electrolytes,
275
+ antioxidants, and has zero calories. It's everything you want from a
276
+ hydration drink — without the sugar.
277
+
278
+ Key finding: In our tests, cactus water delivered 40% more potassium
279
+ per serving than leading coconut water brands.
280
+
281
+ [Read the full article →](https://dolcezero.com/cactus-water-benefits)
282
+ ```
283
+
284
+ ---
285
+
286
+ ## Template Selection Guide
287
+
288
+ | Source Content | Short (< 500 words) | Long (≥ 500 words) |
289
+ |---------------|---------------------|--------------------|
290
+ | Blog post | Tweet + LinkedIn post + email snippet | Thread + LinkedIn article + email snippet |
291
+ | Product page | Tweet + LinkedIn post | Tweet + LinkedIn post + email snippet |
292
+ | Case study | LinkedIn post + email snippet | Thread + LinkedIn article + email snippet |
293
+ | News/update | Tweet | Tweet + LinkedIn post |
294
+
295
+ ## Multi-Channel Workflow
296
+
297
+ For full pipeline execution (1 post → all channels):
298
+
299
+ 1. Fetch post with `wp_get_post`
300
+ 2. Extract elements (title, excerpt, headings, key points, tags, image)
301
+ 3. Apply templates in order: Tweet → Thread (if applicable) → LinkedIn → Email
302
+ 4. Preview all outputs for user review
303
+ 5. Dispatch via: `tw_create_tweet` → `tw_create_thread` → `li_create_post` → `mc_set_campaign_content`
304
+ 6. Report: channels published, content IDs, analytics check schedule
@@ -0,0 +1,96 @@
1
+ ---
2
+ name: wp-linkedin
3
+ description: This skill should be used when the user asks to "publish to LinkedIn",
4
+ "LinkedIn post", "LinkedIn article", "B2B social", "pubblica su LinkedIn",
5
+ "LinkedIn analytics", "LinkedIn engagement", or mentions LinkedIn publishing
6
+ and analytics for WordPress content.
7
+ version: 1.0.0
8
+ tags: [linkedin, social-media, b2b, direct-social]
9
+ ---
10
+
11
+ # WordPress LinkedIn Integration Skill
12
+
13
+ ## Overview
14
+
15
+ Direct LinkedIn publishing connects WordPress content to LinkedIn via the Community Management API v2. This enables B2B-focused content distribution: feed posts for quick updates, long-form articles for blog-to-LinkedIn workflows, and analytics for engagement tracking. The WP REST Bridge provides 5 MCP tools with the `li_*` namespace.
16
+
17
+ ## When to Use
18
+
19
+ - User wants to publish a WordPress blog post to LinkedIn
20
+ - User needs to create LinkedIn feed posts from WordPress content
21
+ - User wants to publish a long-form LinkedIn article from a blog post
22
+ - User asks about LinkedIn post analytics (impressions, clicks, engagement)
23
+ - User needs B2B social media distribution from WordPress
24
+
25
+ ## Decision Tree
26
+
27
+ 1. **What LinkedIn action?**
28
+ - "publish post" / "LinkedIn update" / "feed post" → Posting workflow (Section 1)
29
+ - "article" / "long-form" / "blog to LinkedIn" → Article publishing (Section 2)
30
+ - "analytics" / "engagement" / "impressions" → Analytics (Section 3)
31
+ - "setup" / "configure" / "connect LinkedIn" → Setup guide (Section 4)
32
+
33
+ 2. **Run detection first:**
34
+ ```bash
35
+ node skills/wp-linkedin/scripts/linkedin_inspect.mjs [--cwd=/path/to/project]
36
+ ```
37
+ This identifies configured LinkedIn credentials in WP_SITES_CONFIG.
38
+
39
+ ## Sections
40
+
41
+ ### Section 1: LinkedIn Posting
42
+ See `references/linkedin-posting.md`
43
+ - Feed post creation with text, links, and images
44
+ - Visibility settings (PUBLIC vs CONNECTIONS)
45
+ - Content adaptation from WordPress excerpt/title
46
+ - Hashtag strategy and mention formatting
47
+
48
+ ### Section 2: Article Publishing
49
+ See `references/linkedin-posting.md`
50
+ - Blog post → LinkedIn article conversion
51
+ - HTML content formatting for LinkedIn
52
+ - Thumbnail and media handling
53
+ - Article vs post decision criteria
54
+
55
+ ### Section 3: Analytics
56
+ See `references/linkedin-analytics.md`
57
+ - Post impressions and engagement metrics
58
+ - Click-through rates and share counts
59
+ - Performance comparison across posts
60
+ - B2B content performance benchmarks
61
+
62
+ ### Section 4: Setup Guide
63
+ See `references/linkedin-setup.md`
64
+ - LinkedIn Developer App creation
65
+ - OAuth 2.0 access token generation
66
+ - WP_SITES_CONFIG configuration
67
+ - Required scopes: w_member_social, r_liteprofile
68
+
69
+ ## Reference Files
70
+
71
+ | File | Content |
72
+ |------|---------|
73
+ | `references/linkedin-setup.md` | Developer app, OAuth setup, WP_SITES_CONFIG, scopes |
74
+ | `references/linkedin-posting.md` | Post creation, articles, content adaptation, formatting |
75
+ | `references/linkedin-analytics.md` | Metrics, engagement tracking, performance benchmarks |
76
+
77
+ ## MCP Tools
78
+
79
+ | Tool | Description |
80
+ |------|-------------|
81
+ | `li_get_profile` | Get authenticated LinkedIn user profile |
82
+ | `li_create_post` | Create a LinkedIn feed post (text, link, image) |
83
+ | `li_create_article` | Publish a long-form LinkedIn article |
84
+ | `li_get_analytics` | Get post analytics (impressions, clicks, engagement) |
85
+ | `li_list_posts` | List recent posts by the authenticated user |
86
+
87
+ ## Recommended Agent
88
+
89
+ Use the **`wp-distribution-manager`** agent for multi-channel workflows that combine LinkedIn with Mailchimp, Buffer, SendGrid, and Twitter/X.
90
+
91
+ ## Related Skills
92
+
93
+ - **`wp-twitter`** — Twitter/X direct publishing (awareness channel)
94
+ - **`wp-social-email`** — Mailchimp, Buffer, SendGrid distribution
95
+ - **`wp-content-repurposing`** — Transform blog content into LinkedIn-optimized formats
96
+ - **`wp-content`** — Create source WordPress content for distribution
@@ -0,0 +1,58 @@
1
+ # LinkedIn Analytics Guide
2
+
3
+ ## Available Metrics
4
+
5
+ Use `li_get_analytics` to retrieve post performance data.
6
+
7
+ ### Post-Level Metrics
8
+ - **impressions** — Number of times the post was shown in feeds
9
+ - **clicks** — Total clicks on the post (content, company name, logo)
10
+ - **likes** — Number of likes/reactions
11
+ - **comments** — Number of comments
12
+ - **shares** — Number of reposts
13
+ - **engagement rate** — (clicks + likes + comments + shares) / impressions
14
+
15
+ ### Time Periods
16
+ - `day` — Daily breakdown
17
+ - `month` — Monthly aggregation (default)
18
+
19
+ ## Querying Analytics
20
+
21
+ ### All Posts Summary
22
+ ```
23
+ Tool: li_get_analytics
24
+ Params: {}
25
+ ```
26
+ Returns aggregated statistics across all recent posts.
27
+
28
+ ### Specific Post
29
+ ```
30
+ Tool: li_get_analytics
31
+ Params: { "post_id": "urn:li:share:1234567890" }
32
+ ```
33
+
34
+ ## Performance Benchmarks (B2B)
35
+
36
+ | Metric | Good | Excellent |
37
+ |--------|------|-----------|
38
+ | Engagement rate | 2-4% | >5% |
39
+ | Click-through rate | 0.5-1% | >2% |
40
+ | Impressions per post | 500-2000 | >5000 |
41
+ | Comments per post | 3-10 | >20 |
42
+
43
+ ## Workflow: Post-and-Track
44
+
45
+ 1. Create post with `li_create_post`
46
+ 2. Wait 24-48 hours for meaningful data
47
+ 3. Check metrics with `li_get_analytics`
48
+ 4. Compare against benchmarks above
49
+ 5. Adjust content strategy based on engagement patterns
50
+
51
+ ## Listing Recent Posts
52
+
53
+ ```
54
+ Tool: li_list_posts
55
+ Params: { "count": 20 }
56
+ ```
57
+
58
+ Returns posts with basic metrics for quick overview.
@@ -0,0 +1,53 @@
1
+ # LinkedIn Posting Guide
2
+
3
+ ## Feed Posts vs Articles
4
+
5
+ | Feature | Feed Post (`li_create_post`) | Article (`li_create_article`) |
6
+ |---------|-----|---------|
7
+ | Length | Up to 3,000 chars | Unlimited (long-form) |
8
+ | Format | Plain text + link/image | HTML body |
9
+ | Use case | Quick updates, links, images | Blog-to-LinkedIn, thought leadership |
10
+ | Engagement | Higher reach, lower depth | Lower reach, higher depth |
11
+ | SEO | No | Yes (indexed by search engines) |
12
+
13
+ ## Creating Feed Posts
14
+
15
+ ### Basic Text Post
16
+ ```
17
+ Tool: li_create_post
18
+ Params: { "text": "Excited to share our latest blog post about...", "visibility": "PUBLIC" }
19
+ ```
20
+
21
+ ### Post with Link Share
22
+ ```
23
+ Tool: li_create_post
24
+ Params: { "text": "New article on our blog!", "link_url": "https://mysite.com/blog/post-slug", "visibility": "PUBLIC" }
25
+ ```
26
+
27
+ LinkedIn automatically generates a link preview card with the page's Open Graph metadata.
28
+
29
+ ### Best Practices for Feed Posts
30
+ - **First 2 lines matter** — LinkedIn truncates after ~210 characters with "...see more"
31
+ - **Hashtags**: 3-5 relevant hashtags at the end
32
+ - **Mentions**: Use LinkedIn mention syntax for company pages
33
+ - **Emojis**: Use sparingly for visual breaks
34
+ - **CTA**: Include a clear call-to-action
35
+
36
+ ## Publishing Articles
37
+
38
+ ### Blog-to-LinkedIn Workflow
39
+ 1. Fetch WordPress post: `wp_get_post` with `id`
40
+ 2. Extract: title, content (HTML), featured image URL
41
+ 3. Create article: `li_create_article` with title, body_html, thumbnail_url
42
+
43
+ ### Content Adaptation Tips
44
+ - Remove WordPress-specific shortcodes from HTML
45
+ - Ensure images use absolute URLs
46
+ - Keep title under 100 characters
47
+ - First paragraph should hook the reader
48
+ - Add "Originally published on [site]" at the end
49
+
50
+ ## Visibility Settings
51
+
52
+ - **PUBLIC** — Visible to all LinkedIn users (default)
53
+ - **CONNECTIONS** — Visible only to your connections
@@ -0,0 +1,59 @@
1
+ # LinkedIn Setup Guide
2
+
3
+ ## Prerequisites
4
+
5
+ - LinkedIn Developer account (https://www.linkedin.com/developers/)
6
+ - WordPress site with WP REST Bridge configured
7
+ - LinkedIn personal or company page
8
+
9
+ ## 1. Create LinkedIn Developer App
10
+
11
+ 1. Go to https://www.linkedin.com/developers/apps
12
+ 2. Click "Create app"
13
+ 3. Fill in: App name, LinkedIn Page, Privacy policy URL, App logo
14
+ 4. Under "Products", request access to **Community Management API**
15
+ 5. Wait for approval (usually instant for personal posting)
16
+
17
+ ## 2. Generate OAuth 2.0 Token
18
+
19
+ ### Required Scopes
20
+ - `w_member_social` — Write access to post content
21
+ - `r_liteprofile` — Read basic profile info
22
+ - `openid` — OpenID Connect (for userinfo endpoint)
23
+
24
+ ### Token Generation
25
+ 1. In your LinkedIn app → Auth tab → OAuth 2.0 settings
26
+ 2. Add redirect URL: `https://localhost:8080/callback`
27
+ 3. Use the OAuth 2.0 authorization flow:
28
+ ```
29
+ https://www.linkedin.com/oauth/v2/authorization?response_type=code&client_id={CLIENT_ID}&redirect_uri={REDIRECT_URI}&scope=w_member_social%20r_liteprofile%20openid
30
+ ```
31
+ 4. Exchange the authorization code for an access token
32
+ 5. Token expires in 60 days — set a reminder to refresh
33
+
34
+ ## 3. Configure WP_SITES_CONFIG
35
+
36
+ Add to your site configuration:
37
+
38
+ ```json
39
+ {
40
+ "id": "mysite",
41
+ "url": "https://mysite.com/wp-json/",
42
+ "username": "admin",
43
+ "password": "app-password",
44
+ "linkedin_access_token": "AQV...",
45
+ "linkedin_person_urn": "urn:li:person:ABC123"
46
+ }
47
+ ```
48
+
49
+ ### Finding Your Person URN
50
+ Use `li_get_profile` tool after configuring the access token. The `sub` field in the response contains your person URN.
51
+
52
+ ## 4. Verify Configuration
53
+
54
+ Run the detection script:
55
+ ```bash
56
+ node skills/wp-linkedin/scripts/linkedin_inspect.mjs
57
+ ```
58
+
59
+ Expected output: `linkedin_configured: true` with indicators listing configured credentials.
@@ -0,0 +1,55 @@
1
+ /**
2
+ * linkedin_inspect.mjs — Detect LinkedIn configuration readiness.
3
+ *
4
+ * Checks WP_SITES_CONFIG for LinkedIn credentials.
5
+ *
6
+ * Usage:
7
+ * node linkedin_inspect.mjs [--cwd=/path/to/project]
8
+ *
9
+ * Exit codes:
10
+ * 0 — LinkedIn configuration found
11
+ * 1 — no LinkedIn configuration found
12
+ */
13
+
14
+ import { stdout, exit, argv } from 'node:process';
15
+ import { resolve } from 'node:path';
16
+
17
+ function detectLinkedInConfig() {
18
+ const li = { configured: false, indicators: [] };
19
+ const raw = process.env.WP_SITES_CONFIG;
20
+ if (!raw) return li;
21
+
22
+ let sites;
23
+ try { sites = JSON.parse(raw); } catch { return li; }
24
+ if (!Array.isArray(sites)) return li;
25
+
26
+ for (const site of sites) {
27
+ const label = site.id || site.url || 'unknown';
28
+ if (site.linkedin_access_token) {
29
+ li.configured = true;
30
+ li.indicators.push(`linkedin_access_token configured for ${label}`);
31
+ }
32
+ if (site.linkedin_person_urn) {
33
+ li.indicators.push(`linkedin_person_urn: ${site.linkedin_person_urn} for ${label}`);
34
+ }
35
+ }
36
+ return li;
37
+ }
38
+
39
+ function main() {
40
+ const cwdArg = argv.find(a => a.startsWith('--cwd='));
41
+ const cwd = cwdArg ? resolve(cwdArg.split('=')[1]) : process.cwd();
42
+
43
+ const linkedin = detectLinkedInConfig();
44
+
45
+ const report = {
46
+ linkedin_configured: linkedin.configured,
47
+ linkedin,
48
+ cwd,
49
+ };
50
+
51
+ stdout.write(JSON.stringify(report, null, 2) + '\n');
52
+ exit(linkedin.configured ? 0 : 1);
53
+ }
54
+
55
+ main();