claude-plugin-wordpress-manager 2.1.0 → 2.2.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 +2 -2
- package/CHANGELOG.md +30 -0
- package/agents/wp-content-strategist.md +30 -0
- package/agents/wp-monitoring-agent.md +50 -0
- package/docs/GUIDE.md +249 -15
- package/docs/plans/2026-02-28-wcop-strategic-assessment.md +220 -0
- package/hooks/hooks.json +9 -0
- package/package.json +6 -3
- package/servers/wp-rest-bridge/build/tools/index.d.ts +119 -0
- package/servers/wp-rest-bridge/build/tools/index.js +3 -0
- package/servers/wp-rest-bridge/build/tools/wc-webhooks.d.ts +129 -0
- package/servers/wp-rest-bridge/build/tools/wc-webhooks.js +142 -0
- package/servers/wp-rest-bridge/build/types.d.ts +12 -0
- package/skills/wordpress-router/references/decision-tree.md +7 -3
- package/skills/wp-content/SKILL.md +3 -0
- package/skills/wp-content-repurposing/SKILL.md +96 -0
- package/skills/wp-content-repurposing/references/content-atomization.md +117 -0
- package/skills/wp-content-repurposing/references/email-newsletter.md +136 -0
- package/skills/wp-content-repurposing/references/platform-specs.md +80 -0
- package/skills/wp-content-repurposing/references/social-formats.md +169 -0
- package/skills/wp-content-repurposing/scripts/repurposing_inspect.mjs +140 -0
- package/skills/wp-headless/SKILL.md +1 -0
- package/skills/wp-monitoring/SKILL.md +12 -2
- package/skills/wp-monitoring/references/fleet-monitoring.md +160 -0
- package/skills/wp-monitoring/scripts/monitoring_inspect.mjs +54 -1
- package/skills/wp-webhooks/SKILL.md +107 -0
- package/skills/wp-webhooks/references/integration-recipes.md +176 -0
- package/skills/wp-webhooks/references/payload-formats.md +134 -0
- package/skills/wp-webhooks/references/webhook-security.md +147 -0
- package/skills/wp-webhooks/references/woocommerce-webhooks.md +129 -0
- package/skills/wp-webhooks/references/wordpress-core-webhooks.md +162 -0
- package/skills/wp-webhooks/scripts/webhook_inspect.mjs +157 -0
- package/skills/wp-woocommerce/SKILL.md +1 -0
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# WordPress Core Webhooks
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
WordPress core does not have a built-in webhook management UI like WooCommerce. Instead, you implement outbound notifications by hooking into WordPress action hooks and using `wp_remote_post()` to send HTTP requests. The recommended approach is deploying a mu-plugin.
|
|
6
|
+
|
|
7
|
+
## mu-Plugin Approach
|
|
8
|
+
|
|
9
|
+
Create a mu-plugin at `wp-content/mu-plugins/outbound-webhooks.php`:
|
|
10
|
+
|
|
11
|
+
```php
|
|
12
|
+
<?php
|
|
13
|
+
/**
|
|
14
|
+
* Plugin Name: Outbound Webhooks
|
|
15
|
+
* Description: Sends webhook notifications on content events.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
// Configuration — set these in wp-config.php
|
|
19
|
+
// define('WEBHOOK_URL', 'https://hooks.example.com/wordpress');
|
|
20
|
+
// define('WEBHOOK_SECRET', 'your-secret-key');
|
|
21
|
+
|
|
22
|
+
if (!defined('WEBHOOK_URL') || !WEBHOOK_URL) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Send a webhook notification.
|
|
28
|
+
*/
|
|
29
|
+
function send_webhook($event, $data) {
|
|
30
|
+
$payload = wp_json_encode([
|
|
31
|
+
'event' => $event,
|
|
32
|
+
'timestamp' => gmdate('c'),
|
|
33
|
+
'site_url' => home_url(),
|
|
34
|
+
'data' => $data,
|
|
35
|
+
]);
|
|
36
|
+
|
|
37
|
+
$signature = hash_hmac('sha256', $payload, WEBHOOK_SECRET ?? '');
|
|
38
|
+
|
|
39
|
+
wp_remote_post(WEBHOOK_URL, [
|
|
40
|
+
'timeout' => 5,
|
|
41
|
+
'headers' => [
|
|
42
|
+
'Content-Type' => 'application/json',
|
|
43
|
+
'X-WP-Webhook-Event' => $event,
|
|
44
|
+
'X-WP-Webhook-Signature' => $signature,
|
|
45
|
+
],
|
|
46
|
+
'body' => $payload,
|
|
47
|
+
]);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Hook: Post published or updated.
|
|
52
|
+
*/
|
|
53
|
+
add_action('transition_post_status', function($new_status, $old_status, $post) {
|
|
54
|
+
if ($new_status === 'publish' && $old_status !== 'publish') {
|
|
55
|
+
send_webhook('post.published', [
|
|
56
|
+
'id' => $post->ID,
|
|
57
|
+
'title' => $post->post_title,
|
|
58
|
+
'slug' => $post->post_name,
|
|
59
|
+
'type' => $post->post_type,
|
|
60
|
+
'url' => get_permalink($post),
|
|
61
|
+
]);
|
|
62
|
+
} elseif ($new_status === 'publish' && $old_status === 'publish') {
|
|
63
|
+
send_webhook('post.updated', [
|
|
64
|
+
'id' => $post->ID,
|
|
65
|
+
'title' => $post->post_title,
|
|
66
|
+
'slug' => $post->post_name,
|
|
67
|
+
'type' => $post->post_type,
|
|
68
|
+
'url' => get_permalink($post),
|
|
69
|
+
]);
|
|
70
|
+
}
|
|
71
|
+
}, 10, 3);
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Hook: Term created or edited.
|
|
75
|
+
*/
|
|
76
|
+
add_action('edited_term', function($term_id, $tt_id, $taxonomy) {
|
|
77
|
+
$term = get_term($term_id, $taxonomy);
|
|
78
|
+
send_webhook('term.updated', [
|
|
79
|
+
'id' => $term_id,
|
|
80
|
+
'name' => $term->name,
|
|
81
|
+
'slug' => $term->slug,
|
|
82
|
+
'taxonomy' => $taxonomy,
|
|
83
|
+
]);
|
|
84
|
+
}, 10, 3);
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Hook: User registered.
|
|
88
|
+
*/
|
|
89
|
+
add_action('user_register', function($user_id) {
|
|
90
|
+
$user = get_userdata($user_id);
|
|
91
|
+
send_webhook('user.created', [
|
|
92
|
+
'id' => $user_id,
|
|
93
|
+
'username' => $user->user_login,
|
|
94
|
+
'email' => $user->user_email,
|
|
95
|
+
'role' => implode(', ', $user->roles),
|
|
96
|
+
]);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Hook: Navigation menu updated.
|
|
101
|
+
*/
|
|
102
|
+
add_action('wp_update_nav_menu', function($menu_id) {
|
|
103
|
+
send_webhook('menu.updated', [
|
|
104
|
+
'menu_id' => $menu_id,
|
|
105
|
+
]);
|
|
106
|
+
});
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Key WordPress Action Hooks
|
|
110
|
+
|
|
111
|
+
| Hook | Fires When | Arguments |
|
|
112
|
+
|------|-----------|-----------|
|
|
113
|
+
| `transition_post_status` | Post status changes | $new_status, $old_status, $post |
|
|
114
|
+
| `save_post` | Post is saved (any status) | $post_id, $post, $update |
|
|
115
|
+
| `delete_post` | Post is deleted | $post_id |
|
|
116
|
+
| `edited_term` | Term is created or edited | $term_id, $tt_id, $taxonomy |
|
|
117
|
+
| `user_register` | New user registers | $user_id |
|
|
118
|
+
| `profile_update` | User profile updated | $user_id, $old_user_data |
|
|
119
|
+
| `wp_update_nav_menu` | Navigation menu updated | $menu_id |
|
|
120
|
+
| `updated_option` | Option value changed | $option_name, $old, $new |
|
|
121
|
+
| `activated_plugin` | Plugin activated | $plugin |
|
|
122
|
+
| `switch_theme` | Theme switched | $new_name, $new_theme |
|
|
123
|
+
|
|
124
|
+
## wp-config.php Constants
|
|
125
|
+
|
|
126
|
+
Add these constants to `wp-config.php` for the mu-plugin:
|
|
127
|
+
|
|
128
|
+
```php
|
|
129
|
+
// Primary webhook endpoint
|
|
130
|
+
define('WEBHOOK_URL', 'https://hooks.example.com/wordpress');
|
|
131
|
+
|
|
132
|
+
// HMAC secret for signature verification
|
|
133
|
+
define('WEBHOOK_SECRET', 'your-secret-key-here');
|
|
134
|
+
|
|
135
|
+
// Optional: headless frontend revalidation
|
|
136
|
+
define('HEADLESS_WEBHOOK_URL', 'https://frontend.example.com/api/revalidate');
|
|
137
|
+
define('HEADLESS_WEBHOOK_SECRET', 'revalidation-secret');
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Deployment
|
|
141
|
+
|
|
142
|
+
Deploy the mu-plugin via:
|
|
143
|
+
1. **SSH/SFTP**: Upload to `wp-content/mu-plugins/`
|
|
144
|
+
2. **WP-CLI**: `wp eval 'file_put_contents(WPMU_PLUGIN_DIR . "/outbound-webhooks.php", $code);'`
|
|
145
|
+
3. **Hostinger MCP**: Use file deployment tools
|
|
146
|
+
|
|
147
|
+
mu-plugins load automatically — no activation needed.
|
|
148
|
+
|
|
149
|
+
## Testing
|
|
150
|
+
|
|
151
|
+
Verify webhooks fire correctly:
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
# Check if mu-plugin is loaded
|
|
155
|
+
wp eval "echo defined('WEBHOOK_URL') ? 'configured' : 'not configured';"
|
|
156
|
+
|
|
157
|
+
# Trigger a test by updating a post
|
|
158
|
+
wp post update 1 --post_title="Test Webhook Trigger"
|
|
159
|
+
|
|
160
|
+
# Check for errors in debug.log
|
|
161
|
+
tail -f wp-content/debug.log | grep -i webhook
|
|
162
|
+
```
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* webhook_inspect.mjs — Detect webhook configuration for WordPress projects.
|
|
3
|
+
*
|
|
4
|
+
* Scans for WooCommerce webhooks, mu-plugin webhooks, webhook plugins,
|
|
5
|
+
* and wp-config.php webhook constants.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* node webhook_inspect.mjs [--cwd=/path/to/project]
|
|
9
|
+
*
|
|
10
|
+
* Exit codes:
|
|
11
|
+
* 0 — webhook configuration found
|
|
12
|
+
* 1 — no webhook configuration 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 detectWooCommerceWebhooks(cwd) {
|
|
40
|
+
const indicators = [];
|
|
41
|
+
|
|
42
|
+
// Check for WooCommerce plugin
|
|
43
|
+
const plugins = globDir(join(cwd, 'wp-content', 'plugins'));
|
|
44
|
+
if (plugins.some(p => p.toLowerCase().includes('woocommerce'))) {
|
|
45
|
+
indicators.push('woocommerce_installed');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return { found: indicators.length > 0, indicators };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function detectMuPluginWebhooks(cwd) {
|
|
52
|
+
const indicators = [];
|
|
53
|
+
|
|
54
|
+
const muPlugins = globDir(join(cwd, 'wp-content', 'mu-plugins'));
|
|
55
|
+
for (const file of muPlugins) {
|
|
56
|
+
if (!file.endsWith('.php')) continue;
|
|
57
|
+
const content = readFileSafe(join(cwd, 'wp-content', 'mu-plugins', file));
|
|
58
|
+
if (!content) continue;
|
|
59
|
+
|
|
60
|
+
if (/wp_remote_post/i.test(content) && /webhook|hook|notify|propagat/i.test(content)) {
|
|
61
|
+
indicators.push(`mu_plugin_webhook: ${file}`);
|
|
62
|
+
}
|
|
63
|
+
if (/HEADLESS_WEBHOOK_URL|WEBHOOK_URL/i.test(content)) {
|
|
64
|
+
indicators.push(`mu_plugin_webhook_constant: ${file}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return { found: indicators.length > 0, indicators };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function detectWebhookPlugins(cwd) {
|
|
72
|
+
const indicators = [];
|
|
73
|
+
const plugins = globDir(join(cwd, 'wp-content', 'plugins'));
|
|
74
|
+
const webhookPlugins = [
|
|
75
|
+
'wp-webhooks', 'zapier', 'uncanny-automator', 'automator',
|
|
76
|
+
'wp-fusion', 'webhook', 'notification', 'hookpress',
|
|
77
|
+
];
|
|
78
|
+
|
|
79
|
+
for (const plugin of plugins) {
|
|
80
|
+
if (webhookPlugins.some(wp => plugin.toLowerCase().includes(wp))) {
|
|
81
|
+
indicators.push(`webhook_plugin: ${plugin}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return { found: indicators.length > 0, indicators };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function detectWebhookConstants(cwd) {
|
|
89
|
+
const indicators = [];
|
|
90
|
+
|
|
91
|
+
const wpConfig = readFileSafe(join(cwd, 'wp-config.php'));
|
|
92
|
+
if (wpConfig) {
|
|
93
|
+
if (/WEBHOOK_URL/i.test(wpConfig)) {
|
|
94
|
+
indicators.push('webhook_url_constant');
|
|
95
|
+
}
|
|
96
|
+
if (/WEBHOOK_SECRET/i.test(wpConfig)) {
|
|
97
|
+
indicators.push('webhook_secret_constant');
|
|
98
|
+
}
|
|
99
|
+
if (/HEADLESS_WEBHOOK_URL/i.test(wpConfig)) {
|
|
100
|
+
indicators.push('headless_webhook_url');
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return { found: indicators.length > 0, indicators };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// ---------------------------------------------------------------------------
|
|
108
|
+
// Main
|
|
109
|
+
// ---------------------------------------------------------------------------
|
|
110
|
+
|
|
111
|
+
function main() {
|
|
112
|
+
const cwdArg = argv.find(a => a.startsWith('--cwd='));
|
|
113
|
+
const cwd = cwdArg ? resolve(cwdArg.split('=')[1]) : process.cwd();
|
|
114
|
+
|
|
115
|
+
const wcWebhooks = detectWooCommerceWebhooks(cwd);
|
|
116
|
+
const muPluginWebhooks = detectMuPluginWebhooks(cwd);
|
|
117
|
+
const webhookPlugins = detectWebhookPlugins(cwd);
|
|
118
|
+
const webhookConstants = detectWebhookConstants(cwd);
|
|
119
|
+
|
|
120
|
+
const hasWebhooks = wcWebhooks.found || muPluginWebhooks.found ||
|
|
121
|
+
webhookPlugins.found || webhookConstants.found;
|
|
122
|
+
|
|
123
|
+
const recommendations = [];
|
|
124
|
+
|
|
125
|
+
if (wcWebhooks.found) {
|
|
126
|
+
recommendations.push('WooCommerce detected — use wc_list_webhooks / wc_create_webhook MCP tools for webhook management');
|
|
127
|
+
}
|
|
128
|
+
if (muPluginWebhooks.found) {
|
|
129
|
+
recommendations.push('mu-plugin webhooks detected — review delivery URLs and secrets for security');
|
|
130
|
+
}
|
|
131
|
+
if (!hasWebhooks) {
|
|
132
|
+
recommendations.push('No webhook configuration detected — use wp-webhooks skill to set up outbound notifications');
|
|
133
|
+
}
|
|
134
|
+
if (webhookConstants.found && !webhookConstants.indicators.includes('webhook_secret_constant')) {
|
|
135
|
+
recommendations.push('Webhook URL configured but no secret found — add WEBHOOK_SECRET to wp-config.php for signature verification');
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const report = {
|
|
139
|
+
tool: 'webhook_inspect',
|
|
140
|
+
version: '1.0.0',
|
|
141
|
+
timestamp: new Date().toISOString(),
|
|
142
|
+
cwd,
|
|
143
|
+
found: hasWebhooks,
|
|
144
|
+
areas: {
|
|
145
|
+
wc_webhooks: wcWebhooks,
|
|
146
|
+
mu_plugin_webhooks: muPluginWebhooks,
|
|
147
|
+
webhook_plugins: webhookPlugins,
|
|
148
|
+
webhook_constants: webhookConstants,
|
|
149
|
+
},
|
|
150
|
+
recommendations,
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
stdout.write(JSON.stringify(report, null, 2) + '\n');
|
|
154
|
+
exit(hasWebhooks ? 0 : 1);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
main();
|
|
@@ -108,3 +108,4 @@ For complex multi-step WooCommerce operations, use the `wp-ecommerce-manager` ag
|
|
|
108
108
|
- `wp-deploy` — Deploy WooCommerce store changes to production
|
|
109
109
|
- `wp-audit` — Audit WooCommerce store security and performance
|
|
110
110
|
- `wp-backup` — Backup WooCommerce database and uploads
|
|
111
|
+
- `wp-webhooks` — WooCommerce webhook management (order/product/customer event notifications)
|