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.
Files changed (33) hide show
  1. package/.claude-plugin/plugin.json +2 -2
  2. package/CHANGELOG.md +30 -0
  3. package/agents/wp-content-strategist.md +30 -0
  4. package/agents/wp-monitoring-agent.md +50 -0
  5. package/docs/GUIDE.md +249 -15
  6. package/docs/plans/2026-02-28-wcop-strategic-assessment.md +220 -0
  7. package/hooks/hooks.json +9 -0
  8. package/package.json +6 -3
  9. package/servers/wp-rest-bridge/build/tools/index.d.ts +119 -0
  10. package/servers/wp-rest-bridge/build/tools/index.js +3 -0
  11. package/servers/wp-rest-bridge/build/tools/wc-webhooks.d.ts +129 -0
  12. package/servers/wp-rest-bridge/build/tools/wc-webhooks.js +142 -0
  13. package/servers/wp-rest-bridge/build/types.d.ts +12 -0
  14. package/skills/wordpress-router/references/decision-tree.md +7 -3
  15. package/skills/wp-content/SKILL.md +3 -0
  16. package/skills/wp-content-repurposing/SKILL.md +96 -0
  17. package/skills/wp-content-repurposing/references/content-atomization.md +117 -0
  18. package/skills/wp-content-repurposing/references/email-newsletter.md +136 -0
  19. package/skills/wp-content-repurposing/references/platform-specs.md +80 -0
  20. package/skills/wp-content-repurposing/references/social-formats.md +169 -0
  21. package/skills/wp-content-repurposing/scripts/repurposing_inspect.mjs +140 -0
  22. package/skills/wp-headless/SKILL.md +1 -0
  23. package/skills/wp-monitoring/SKILL.md +12 -2
  24. package/skills/wp-monitoring/references/fleet-monitoring.md +160 -0
  25. package/skills/wp-monitoring/scripts/monitoring_inspect.mjs +54 -1
  26. package/skills/wp-webhooks/SKILL.md +107 -0
  27. package/skills/wp-webhooks/references/integration-recipes.md +176 -0
  28. package/skills/wp-webhooks/references/payload-formats.md +134 -0
  29. package/skills/wp-webhooks/references/webhook-security.md +147 -0
  30. package/skills/wp-webhooks/references/woocommerce-webhooks.md +129 -0
  31. package/skills/wp-webhooks/references/wordpress-core-webhooks.md +162 -0
  32. package/skills/wp-webhooks/scripts/webhook_inspect.mjs +157 -0
  33. 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)