claude-plugin-wordpress-manager 2.6.0 → 2.9.1
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/CHANGELOG.md +61 -0
- package/agents/wp-monitoring-agent.md +44 -0
- package/agents/wp-site-manager.md +19 -0
- package/docs/GUIDE.md +145 -14
- package/docs/plans/2026-03-01-tier4-5-implementation.md +1783 -0
- package/docs/plans/2026-03-01-tier4-5-observability-automation-design.md +426 -0
- package/docs/plans/2026-03-01-wcop-reassessment-v2.6.0.md +403 -0
- package/hooks/hooks.json +9 -0
- package/package.json +10 -3
- package/servers/wp-rest-bridge/build/tools/cwv.d.ts +3 -0
- package/servers/wp-rest-bridge/build/tools/cwv.js +196 -0
- package/servers/wp-rest-bridge/build/tools/ga4.d.ts +3 -0
- package/servers/wp-rest-bridge/build/tools/ga4.js +323 -0
- package/servers/wp-rest-bridge/build/tools/index.js +15 -0
- package/servers/wp-rest-bridge/build/tools/plausible.d.ts +3 -0
- package/servers/wp-rest-bridge/build/tools/plausible.js +207 -0
- package/servers/wp-rest-bridge/build/tools/slack.d.ts +3 -0
- package/servers/wp-rest-bridge/build/tools/slack.js +129 -0
- package/servers/wp-rest-bridge/build/tools/wc-workflows.d.ts +3 -0
- package/servers/wp-rest-bridge/build/tools/wc-workflows.js +222 -0
- package/servers/wp-rest-bridge/build/wordpress.d.ts +18 -0
- package/servers/wp-rest-bridge/build/wordpress.js +139 -0
- package/skills/wordpress-router/SKILL.md +1 -1
- package/skills/wordpress-router/references/decision-tree.md +8 -2
- package/skills/wp-alerting/SKILL.md +142 -0
- package/skills/wp-alerting/references/alert-thresholds.md +79 -0
- package/skills/wp-alerting/references/escalation-paths.md +92 -0
- package/skills/wp-alerting/references/report-scheduling.md +142 -0
- package/skills/wp-alerting/references/slack-integration.md +109 -0
- package/skills/wp-alerting/scripts/alerting_inspect.mjs +150 -0
- package/skills/wp-analytics/SKILL.md +158 -0
- package/skills/wp-analytics/references/analytics-dashboards.md +83 -0
- package/skills/wp-analytics/references/cwv-monitoring.md +101 -0
- package/skills/wp-analytics/references/ga4-integration.md +76 -0
- package/skills/wp-analytics/references/plausible-setup.md +92 -0
- package/skills/wp-analytics/references/traffic-attribution.md +92 -0
- package/skills/wp-analytics/scripts/analytics_inspect.mjs +153 -0
- package/skills/wp-content-attribution/SKILL.md +1 -0
- package/skills/wp-content-optimization/SKILL.md +1 -0
- package/skills/wp-content-workflows/SKILL.md +142 -0
- package/skills/wp-content-workflows/references/content-lifecycle-hooks.md +131 -0
- package/skills/wp-content-workflows/references/multi-channel-actions.md +151 -0
- package/skills/wp-content-workflows/references/schedule-triggers.md +118 -0
- package/skills/wp-content-workflows/references/trigger-management.md +159 -0
- package/skills/wp-content-workflows/references/wp-action-hooks.md +114 -0
- package/skills/wp-content-workflows/scripts/workflow_inspect.mjs +202 -0
- package/skills/wp-monitoring/SKILL.md +2 -0
- package/skills/wp-search-console/SKILL.md +1 -0
- package/skills/wp-social-email/SKILL.md +1 -0
- package/skills/wp-webhooks/SKILL.md +1 -0
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { hasSlackWebhook, getSlackWebhookUrl, hasSlackBot, makeSlackBotRequest } from '../wordpress.js';
|
|
2
|
+
import axios from 'axios';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
// ── Zod Schemas ─────────────────────────────────────────────────
|
|
5
|
+
const slackSendAlertSchema = z.object({
|
|
6
|
+
text: z.string().describe('Alert message text (supports Slack markdown)'),
|
|
7
|
+
channel: z.string().optional().describe('Override channel (webhook default if omitted)'),
|
|
8
|
+
username: z.string().optional().default('WP Monitor').describe('Bot display name'),
|
|
9
|
+
icon_emoji: z.string().optional().default(':warning:').describe('Bot icon emoji'),
|
|
10
|
+
blocks: z.array(z.any()).optional().describe('Slack Block Kit blocks (optional, overrides text for rich formatting)'),
|
|
11
|
+
}).strict();
|
|
12
|
+
const slackSendMessageSchema = z.object({
|
|
13
|
+
channel: z.string().describe('Channel ID or name (e.g., #general or C01234ABC)'),
|
|
14
|
+
text: z.string().describe('Message text (required as fallback even with blocks)'),
|
|
15
|
+
blocks: z.array(z.any()).optional().describe('Slack Block Kit blocks for rich formatting'),
|
|
16
|
+
thread_ts: z.string().optional().describe('Thread timestamp for threaded replies'),
|
|
17
|
+
}).strict();
|
|
18
|
+
const slackListChannelsSchema = z.object({
|
|
19
|
+
limit: z.number().optional().default(100).describe('Max channels to return (default 100)'),
|
|
20
|
+
types: z.string().optional().default('public_channel').describe('Channel types (default: public_channel)'),
|
|
21
|
+
}).strict();
|
|
22
|
+
// ── Tool Definitions ────────────────────────────────────────────
|
|
23
|
+
export const slackTools = [
|
|
24
|
+
{
|
|
25
|
+
name: "slack_send_alert",
|
|
26
|
+
description: "Sends an alert notification to Slack via Incoming Webhook (zero-config, no Bot Token needed)",
|
|
27
|
+
inputSchema: {
|
|
28
|
+
type: "object",
|
|
29
|
+
properties: {
|
|
30
|
+
text: { type: "string", description: "Alert message text (Slack markdown)" },
|
|
31
|
+
channel: { type: "string", description: "Override channel (optional)" },
|
|
32
|
+
username: { type: "string", description: "Bot display name (default: WP Monitor)" },
|
|
33
|
+
icon_emoji: { type: "string", description: "Bot icon emoji (default: :warning:)" },
|
|
34
|
+
blocks: { type: "array", items: { type: "object" }, description: "Block Kit blocks (optional)" },
|
|
35
|
+
},
|
|
36
|
+
required: ["text"],
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
name: "slack_send_message",
|
|
41
|
+
description: "Sends a message to a Slack channel via Bot Token (supports Block Kit, threads)",
|
|
42
|
+
inputSchema: {
|
|
43
|
+
type: "object",
|
|
44
|
+
properties: {
|
|
45
|
+
channel: { type: "string", description: "Channel ID or name" },
|
|
46
|
+
text: { type: "string", description: "Message text (fallback for blocks)" },
|
|
47
|
+
blocks: { type: "array", items: { type: "object" }, description: "Block Kit blocks" },
|
|
48
|
+
thread_ts: { type: "string", description: "Thread timestamp for replies" },
|
|
49
|
+
},
|
|
50
|
+
required: ["channel", "text"],
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
name: "slack_list_channels",
|
|
55
|
+
description: "Lists Slack workspace channels via Bot Token",
|
|
56
|
+
inputSchema: {
|
|
57
|
+
type: "object",
|
|
58
|
+
properties: {
|
|
59
|
+
limit: { type: "number", description: "Max channels (default 100)" },
|
|
60
|
+
types: { type: "string", description: "Channel types (default: public_channel)" },
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
];
|
|
65
|
+
// ── Handlers ────────────────────────────────────────────────────
|
|
66
|
+
export const slackHandlers = {
|
|
67
|
+
slack_send_alert: async (params) => {
|
|
68
|
+
if (!hasSlackWebhook()) {
|
|
69
|
+
return { toolResult: { isError: true, content: [{ type: "text", text: "Slack webhook not configured. Add slack_webhook_url to WP_SITES_CONFIG." }] } };
|
|
70
|
+
}
|
|
71
|
+
try {
|
|
72
|
+
const { text, channel, username, icon_emoji, blocks } = params;
|
|
73
|
+
const webhookUrl = getSlackWebhookUrl();
|
|
74
|
+
const payload = { text, username: username || 'WP Monitor', icon_emoji: icon_emoji || ':warning:' };
|
|
75
|
+
if (channel)
|
|
76
|
+
payload.channel = channel;
|
|
77
|
+
if (blocks)
|
|
78
|
+
payload.blocks = blocks;
|
|
79
|
+
await axios.post(webhookUrl, payload, { timeout: 10000 });
|
|
80
|
+
return { toolResult: { content: [{ type: "text", text: "Alert sent successfully to Slack." }] } };
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
const errorMessage = error.response?.data || error.message;
|
|
84
|
+
return { toolResult: { isError: true, content: [{ type: "text", text: `Error sending Slack alert: ${typeof errorMessage === 'string' ? errorMessage : JSON.stringify(errorMessage)}` }] } };
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
slack_send_message: async (params) => {
|
|
88
|
+
if (!hasSlackBot()) {
|
|
89
|
+
return { toolResult: { isError: true, content: [{ type: "text", text: "Slack Bot not configured. Add slack_bot_token to WP_SITES_CONFIG." }] } };
|
|
90
|
+
}
|
|
91
|
+
try {
|
|
92
|
+
const { channel, text, blocks, thread_ts } = params;
|
|
93
|
+
const payload = { channel, text };
|
|
94
|
+
if (blocks)
|
|
95
|
+
payload.blocks = blocks;
|
|
96
|
+
if (thread_ts)
|
|
97
|
+
payload.thread_ts = thread_ts;
|
|
98
|
+
const response = await makeSlackBotRequest('POST', 'chat.postMessage', payload);
|
|
99
|
+
if (!response.ok) {
|
|
100
|
+
return { toolResult: { isError: true, content: [{ type: "text", text: `Slack API error: ${response.error}` }] } };
|
|
101
|
+
}
|
|
102
|
+
return { toolResult: { content: [{ type: "text", text: JSON.stringify({ ok: true, channel: response.channel, ts: response.ts }, null, 2) }] } };
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
const errorMessage = error.response?.data?.error || error.message;
|
|
106
|
+
return { toolResult: { isError: true, content: [{ type: "text", text: `Error sending Slack message: ${errorMessage}` }] } };
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
slack_list_channels: async (params) => {
|
|
110
|
+
if (!hasSlackBot()) {
|
|
111
|
+
return { toolResult: { isError: true, content: [{ type: "text", text: "Slack Bot not configured. Add slack_bot_token to WP_SITES_CONFIG." }] } };
|
|
112
|
+
}
|
|
113
|
+
try {
|
|
114
|
+
const { limit, types } = params;
|
|
115
|
+
const response = await makeSlackBotRequest('GET', `conversations.list?limit=${limit || 100}&types=${types || 'public_channel'}`);
|
|
116
|
+
if (!response.ok) {
|
|
117
|
+
return { toolResult: { isError: true, content: [{ type: "text", text: `Slack API error: ${response.error}` }] } };
|
|
118
|
+
}
|
|
119
|
+
const channels = (response.channels || []).map((ch) => ({
|
|
120
|
+
id: ch.id, name: ch.name, topic: ch.topic?.value, num_members: ch.num_members,
|
|
121
|
+
}));
|
|
122
|
+
return { toolResult: { content: [{ type: "text", text: JSON.stringify(channels, null, 2) }] } };
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
const errorMessage = error.response?.data?.error || error.message;
|
|
126
|
+
return { toolResult: { isError: true, content: [{ type: "text", text: `Error listing Slack channels: ${errorMessage}` }] } };
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
};
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import { makeWordPressRequest } from '../wordpress.js';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
const WF_NAMESPACE = 'wp-manager/v1';
|
|
4
|
+
// ── Zod Schemas ─────────────────────────────────────────────────
|
|
5
|
+
const actionSchema = z.object({
|
|
6
|
+
channel: z.enum(['slack', 'email', 'webhook']).describe('Notification channel'),
|
|
7
|
+
template: z.string().describe('Message template (supports {{variable}} placeholders)'),
|
|
8
|
+
recipients: z.array(z.string()).describe('Recipients (emails, Slack channels, or webhook URLs)'),
|
|
9
|
+
}).strict();
|
|
10
|
+
const conditionsSchema = z.record(z.any())
|
|
11
|
+
.describe('Trigger conditions object (structure depends on trigger type)');
|
|
12
|
+
const wfListTriggersSchema = z.object({
|
|
13
|
+
status: z.enum(['active', 'inactive', 'all']).optional()
|
|
14
|
+
.describe('Filter by trigger status (default: all)'),
|
|
15
|
+
type: z.enum(['schedule', 'hook', 'content']).optional()
|
|
16
|
+
.describe('Filter by trigger type'),
|
|
17
|
+
limit: z.number().optional().default(50)
|
|
18
|
+
.describe('Max triggers to return (default 50)'),
|
|
19
|
+
}).strict();
|
|
20
|
+
const wfCreateTriggerSchema = z.object({
|
|
21
|
+
name: z.string().describe('Trigger name (human-readable identifier)'),
|
|
22
|
+
type: z.enum(['schedule', 'hook', 'content'])
|
|
23
|
+
.describe('Trigger type: schedule (cron-based), hook (WP action/filter), content (post lifecycle)'),
|
|
24
|
+
conditions: conditionsSchema,
|
|
25
|
+
actions: z.array(actionSchema).min(1)
|
|
26
|
+
.describe('Actions to execute when trigger fires'),
|
|
27
|
+
status: z.enum(['active', 'inactive']).optional()
|
|
28
|
+
.describe('Initial status (default: active)'),
|
|
29
|
+
description: z.string().optional()
|
|
30
|
+
.describe('Human-readable description of what this trigger does'),
|
|
31
|
+
}).strict();
|
|
32
|
+
const wfUpdateTriggerSchema = z.object({
|
|
33
|
+
trigger_id: z.number().describe('Trigger ID to update'),
|
|
34
|
+
name: z.string().optional().describe('Updated trigger name'),
|
|
35
|
+
status: z.enum(['active', 'inactive']).optional()
|
|
36
|
+
.describe('Updated trigger status'),
|
|
37
|
+
conditions: conditionsSchema.optional(),
|
|
38
|
+
actions: z.array(actionSchema).optional()
|
|
39
|
+
.describe('Updated actions array'),
|
|
40
|
+
description: z.string().optional()
|
|
41
|
+
.describe('Updated description'),
|
|
42
|
+
}).strict();
|
|
43
|
+
const wfDeleteTriggerSchema = z.object({
|
|
44
|
+
trigger_id: z.number().describe('Trigger ID to delete'),
|
|
45
|
+
}).strict();
|
|
46
|
+
// ── Tool Definitions ────────────────────────────────────────────
|
|
47
|
+
export const wcWorkflowTools = [
|
|
48
|
+
{
|
|
49
|
+
name: "wf_list_triggers",
|
|
50
|
+
description: "Lists all configured workflow triggers (scheduled events, WP hooks, content lifecycle) with their status, conditions, and actions",
|
|
51
|
+
inputSchema: {
|
|
52
|
+
type: "object",
|
|
53
|
+
properties: {
|
|
54
|
+
status: {
|
|
55
|
+
type: "string",
|
|
56
|
+
enum: ["active", "inactive", "all"],
|
|
57
|
+
description: "Filter by trigger status (default: all)",
|
|
58
|
+
},
|
|
59
|
+
type: {
|
|
60
|
+
type: "string",
|
|
61
|
+
enum: ["schedule", "hook", "content"],
|
|
62
|
+
description: "Filter by trigger type",
|
|
63
|
+
},
|
|
64
|
+
limit: {
|
|
65
|
+
type: "number",
|
|
66
|
+
description: "Max triggers to return (default 50)",
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
name: "wf_create_trigger",
|
|
73
|
+
description: "Creates a new workflow trigger that fires Slack alerts, emails, or webhook notifications on scheduled events, WP hooks, or content lifecycle changes",
|
|
74
|
+
inputSchema: {
|
|
75
|
+
type: "object",
|
|
76
|
+
properties: {
|
|
77
|
+
name: { type: "string", description: "Trigger name (human-readable identifier)" },
|
|
78
|
+
type: {
|
|
79
|
+
type: "string",
|
|
80
|
+
enum: ["schedule", "hook", "content"],
|
|
81
|
+
description: "Trigger type: schedule (cron), hook (WP action/filter), content (post lifecycle)",
|
|
82
|
+
},
|
|
83
|
+
conditions: {
|
|
84
|
+
type: "object",
|
|
85
|
+
description: "Trigger conditions (structure depends on type)",
|
|
86
|
+
},
|
|
87
|
+
actions: {
|
|
88
|
+
type: "array",
|
|
89
|
+
items: {
|
|
90
|
+
type: "object",
|
|
91
|
+
properties: {
|
|
92
|
+
channel: { type: "string", enum: ["slack", "email", "webhook"], description: "Notification channel" },
|
|
93
|
+
template: { type: "string", description: "Message template (supports {{variable}} placeholders)" },
|
|
94
|
+
recipients: { type: "array", items: { type: "string" }, description: "Recipients list" },
|
|
95
|
+
},
|
|
96
|
+
required: ["channel", "template", "recipients"],
|
|
97
|
+
},
|
|
98
|
+
description: "Actions to execute when trigger fires",
|
|
99
|
+
},
|
|
100
|
+
status: {
|
|
101
|
+
type: "string",
|
|
102
|
+
enum: ["active", "inactive"],
|
|
103
|
+
description: "Initial status (default: active)",
|
|
104
|
+
},
|
|
105
|
+
description: { type: "string", description: "What this trigger does" },
|
|
106
|
+
},
|
|
107
|
+
required: ["name", "type", "conditions", "actions"],
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
name: "wf_update_trigger",
|
|
112
|
+
description: "Updates an existing workflow trigger (name, status, conditions, actions, or description)",
|
|
113
|
+
inputSchema: {
|
|
114
|
+
type: "object",
|
|
115
|
+
properties: {
|
|
116
|
+
trigger_id: { type: "number", description: "Trigger ID to update" },
|
|
117
|
+
name: { type: "string", description: "Updated trigger name" },
|
|
118
|
+
status: {
|
|
119
|
+
type: "string",
|
|
120
|
+
enum: ["active", "inactive"],
|
|
121
|
+
description: "Updated trigger status",
|
|
122
|
+
},
|
|
123
|
+
conditions: {
|
|
124
|
+
type: "object",
|
|
125
|
+
description: "Updated trigger conditions",
|
|
126
|
+
},
|
|
127
|
+
actions: {
|
|
128
|
+
type: "array",
|
|
129
|
+
items: {
|
|
130
|
+
type: "object",
|
|
131
|
+
properties: {
|
|
132
|
+
channel: { type: "string", enum: ["slack", "email", "webhook"], description: "Notification channel" },
|
|
133
|
+
template: { type: "string", description: "Message template" },
|
|
134
|
+
recipients: { type: "array", items: { type: "string" }, description: "Recipients list" },
|
|
135
|
+
},
|
|
136
|
+
required: ["channel", "template", "recipients"],
|
|
137
|
+
},
|
|
138
|
+
description: "Updated actions array",
|
|
139
|
+
},
|
|
140
|
+
description: { type: "string", description: "Updated description" },
|
|
141
|
+
},
|
|
142
|
+
required: ["trigger_id"],
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
name: "wf_delete_trigger",
|
|
147
|
+
description: "Deletes a workflow trigger (stops all associated automation and notifications)",
|
|
148
|
+
inputSchema: {
|
|
149
|
+
type: "object",
|
|
150
|
+
properties: {
|
|
151
|
+
trigger_id: { type: "number", description: "Trigger ID to delete" },
|
|
152
|
+
},
|
|
153
|
+
required: ["trigger_id"],
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
];
|
|
157
|
+
// ── Handlers ────────────────────────────────────────────────────
|
|
158
|
+
export const wcWorkflowHandlers = {
|
|
159
|
+
wf_list_triggers: async (params) => {
|
|
160
|
+
try {
|
|
161
|
+
const queryParts = [];
|
|
162
|
+
if (params.status && params.status !== 'all')
|
|
163
|
+
queryParts.push(`status=${params.status}`);
|
|
164
|
+
if (params.type)
|
|
165
|
+
queryParts.push(`type=${params.type}`);
|
|
166
|
+
if (params.limit)
|
|
167
|
+
queryParts.push(`per_page=${params.limit}`);
|
|
168
|
+
const query = queryParts.length > 0 ? `?${queryParts.join('&')}` : '';
|
|
169
|
+
const response = await makeWordPressRequest('GET', `workflows${query}`, undefined, {
|
|
170
|
+
namespace: WF_NAMESPACE,
|
|
171
|
+
});
|
|
172
|
+
return { toolResult: { content: [{ type: "text", text: JSON.stringify(response, null, 2) }] } };
|
|
173
|
+
}
|
|
174
|
+
catch (error) {
|
|
175
|
+
const errorMessage = error.response?.data?.message || error.message;
|
|
176
|
+
return { toolResult: { isError: true, content: [{ type: "text", text: `Error listing workflow triggers: ${errorMessage}` }] } };
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
wf_create_trigger: async (params) => {
|
|
180
|
+
try {
|
|
181
|
+
const { name, type, conditions, actions, status, description } = params;
|
|
182
|
+
const body = { name, type, conditions, actions };
|
|
183
|
+
if (status)
|
|
184
|
+
body.status = status;
|
|
185
|
+
if (description)
|
|
186
|
+
body.description = description;
|
|
187
|
+
const response = await makeWordPressRequest('POST', 'workflows', body, {
|
|
188
|
+
namespace: WF_NAMESPACE,
|
|
189
|
+
});
|
|
190
|
+
return { toolResult: { content: [{ type: "text", text: JSON.stringify(response, null, 2) }] } };
|
|
191
|
+
}
|
|
192
|
+
catch (error) {
|
|
193
|
+
const errorMessage = error.response?.data?.message || error.message;
|
|
194
|
+
return { toolResult: { isError: true, content: [{ type: "text", text: `Error creating workflow trigger: ${errorMessage}` }] } };
|
|
195
|
+
}
|
|
196
|
+
},
|
|
197
|
+
wf_update_trigger: async (params) => {
|
|
198
|
+
try {
|
|
199
|
+
const { trigger_id, ...updates } = params;
|
|
200
|
+
const response = await makeWordPressRequest('PUT', `workflows/${trigger_id}`, updates, {
|
|
201
|
+
namespace: WF_NAMESPACE,
|
|
202
|
+
});
|
|
203
|
+
return { toolResult: { content: [{ type: "text", text: JSON.stringify(response, null, 2) }] } };
|
|
204
|
+
}
|
|
205
|
+
catch (error) {
|
|
206
|
+
const errorMessage = error.response?.data?.message || error.message;
|
|
207
|
+
return { toolResult: { isError: true, content: [{ type: "text", text: `Error updating workflow trigger: ${errorMessage}` }] } };
|
|
208
|
+
}
|
|
209
|
+
},
|
|
210
|
+
wf_delete_trigger: async (params) => {
|
|
211
|
+
try {
|
|
212
|
+
const response = await makeWordPressRequest('DELETE', `workflows/${params.trigger_id}?force=true`, undefined, {
|
|
213
|
+
namespace: WF_NAMESPACE,
|
|
214
|
+
});
|
|
215
|
+
return { toolResult: { content: [{ type: "text", text: JSON.stringify(response, null, 2) }] } };
|
|
216
|
+
}
|
|
217
|
+
catch (error) {
|
|
218
|
+
const errorMessage = error.response?.data?.message || error.message;
|
|
219
|
+
return { toolResult: { isError: true, content: [{ type: "text", text: `Error deleting workflow trigger: ${errorMessage}` }] } };
|
|
220
|
+
}
|
|
221
|
+
},
|
|
222
|
+
};
|
|
@@ -16,6 +16,13 @@ interface SiteConfig {
|
|
|
16
16
|
sendgrid_api_key?: string;
|
|
17
17
|
gsc_service_account_key?: string;
|
|
18
18
|
gsc_site_url?: string;
|
|
19
|
+
ga4_property_id?: string;
|
|
20
|
+
ga4_service_account_key?: string;
|
|
21
|
+
plausible_api_key?: string;
|
|
22
|
+
plausible_base_url?: string;
|
|
23
|
+
google_api_key?: string;
|
|
24
|
+
slack_webhook_url?: string;
|
|
25
|
+
slack_bot_token?: string;
|
|
19
26
|
}
|
|
20
27
|
/**
|
|
21
28
|
* Parse WP_SITES_CONFIG JSON and initialize all site clients
|
|
@@ -83,6 +90,17 @@ export declare function makeSendGridRequest(method: string, endpoint: string, da
|
|
|
83
90
|
export declare function hasGSC(siteId?: string): boolean;
|
|
84
91
|
export declare function getGSCSiteUrl(siteId?: string): string;
|
|
85
92
|
export declare function getGSCAuth(siteId?: string): Promise<any>;
|
|
93
|
+
export declare function hasGA4(siteId?: string): boolean;
|
|
94
|
+
export declare function getGA4PropertyId(siteId?: string): string;
|
|
95
|
+
export declare function getGA4Auth(siteId?: string): Promise<any>;
|
|
96
|
+
export declare function hasPlausible(siteId?: string): boolean;
|
|
97
|
+
export declare function makePlausibleRequest(method: string, endpoint: string, params?: Record<string, any>, siteId?: string): Promise<any>;
|
|
98
|
+
export declare function hasGoogleApiKey(siteId?: string): boolean;
|
|
99
|
+
export declare function getGoogleApiKey(siteId?: string): string;
|
|
100
|
+
export declare function hasSlackWebhook(siteId?: string): boolean;
|
|
101
|
+
export declare function getSlackWebhookUrl(siteId?: string): string;
|
|
102
|
+
export declare function hasSlackBot(siteId?: string): boolean;
|
|
103
|
+
export declare function makeSlackBotRequest(method: string, endpoint: string, data?: Record<string, any>, siteId?: string): Promise<any>;
|
|
86
104
|
/**
|
|
87
105
|
* Search the WordPress.org Plugin Repository
|
|
88
106
|
*/
|
|
@@ -37,6 +37,8 @@ const wcSiteClients = new Map();
|
|
|
37
37
|
const mcSiteClients = new Map();
|
|
38
38
|
const bufSiteClients = new Map();
|
|
39
39
|
const sgSiteClients = new Map();
|
|
40
|
+
const plSiteClients = new Map();
|
|
41
|
+
const slackBotClients = new Map();
|
|
40
42
|
let activeSiteId = '';
|
|
41
43
|
const parsedSiteConfigs = new Map();
|
|
42
44
|
const MAX_CONCURRENT_PER_SITE = 5;
|
|
@@ -101,6 +103,14 @@ export async function initWordPress() {
|
|
|
101
103
|
await initSendGridClient(site.id, site.sendgrid_api_key);
|
|
102
104
|
logToStderr(`Initialized SendGrid for site: ${site.id}`);
|
|
103
105
|
}
|
|
106
|
+
if (site.plausible_api_key) {
|
|
107
|
+
await initPlausibleClient(site.id, site.plausible_api_key, site.plausible_base_url);
|
|
108
|
+
logToStderr(`Initialized Plausible for site: ${site.id}`);
|
|
109
|
+
}
|
|
110
|
+
if (site.slack_bot_token) {
|
|
111
|
+
await initSlackBotClient(site.id, site.slack_bot_token);
|
|
112
|
+
logToStderr(`Initialized Slack Bot for site: ${site.id}`);
|
|
113
|
+
}
|
|
104
114
|
}
|
|
105
115
|
activeSiteId = defaultSite || sites[0].id;
|
|
106
116
|
logToStderr(`Active site: ${activeSiteId}`);
|
|
@@ -203,6 +213,27 @@ async function initSendGridClient(id, apiKey) {
|
|
|
203
213
|
});
|
|
204
214
|
sgSiteClients.set(id, client);
|
|
205
215
|
}
|
|
216
|
+
async function initPlausibleClient(id, apiKey, baseUrl) {
|
|
217
|
+
const client = axios.create({
|
|
218
|
+
baseURL: (baseUrl || 'https://plausible.io') + '/api/v1/',
|
|
219
|
+
headers: {
|
|
220
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
221
|
+
},
|
|
222
|
+
timeout: DEFAULT_TIMEOUT_MS,
|
|
223
|
+
});
|
|
224
|
+
plSiteClients.set(id, client);
|
|
225
|
+
}
|
|
226
|
+
async function initSlackBotClient(id, botToken) {
|
|
227
|
+
const client = axios.create({
|
|
228
|
+
baseURL: 'https://slack.com/api/',
|
|
229
|
+
headers: {
|
|
230
|
+
'Authorization': `Bearer ${botToken}`,
|
|
231
|
+
'Content-Type': 'application/json; charset=utf-8',
|
|
232
|
+
},
|
|
233
|
+
timeout: DEFAULT_TIMEOUT_MS,
|
|
234
|
+
});
|
|
235
|
+
slackBotClients.set(id, client);
|
|
236
|
+
}
|
|
206
237
|
// ── Site Management ──────────────────────────────────────────────────
|
|
207
238
|
/**
|
|
208
239
|
* Get the active site's client, or a specific site's client
|
|
@@ -508,6 +539,114 @@ export async function getGSCAuth(siteId) {
|
|
|
508
539
|
gscAuthClients.set(id, authClient);
|
|
509
540
|
return authClient;
|
|
510
541
|
}
|
|
542
|
+
// ── Google Analytics 4 Interface ─────────────────────────────────
|
|
543
|
+
const ga4AuthClients = new Map();
|
|
544
|
+
export function hasGA4(siteId) {
|
|
545
|
+
const id = siteId || activeSiteId;
|
|
546
|
+
const sites = JSON.parse(process.env.WP_SITES_CONFIG || '[]');
|
|
547
|
+
const site = sites.find((s) => s.id === id);
|
|
548
|
+
return !!(site?.ga4_property_id && site?.ga4_service_account_key);
|
|
549
|
+
}
|
|
550
|
+
export function getGA4PropertyId(siteId) {
|
|
551
|
+
const id = siteId || activeSiteId;
|
|
552
|
+
const sites = JSON.parse(process.env.WP_SITES_CONFIG || '[]');
|
|
553
|
+
const site = sites.find((s) => s.id === id);
|
|
554
|
+
if (!site?.ga4_property_id) {
|
|
555
|
+
throw new Error(`GA4 property not configured for site "${id}". Add ga4_property_id to WP_SITES_CONFIG.`);
|
|
556
|
+
}
|
|
557
|
+
return site.ga4_property_id;
|
|
558
|
+
}
|
|
559
|
+
export async function getGA4Auth(siteId) {
|
|
560
|
+
const id = siteId || activeSiteId;
|
|
561
|
+
if (ga4AuthClients.has(id))
|
|
562
|
+
return ga4AuthClients.get(id);
|
|
563
|
+
const sites = JSON.parse(process.env.WP_SITES_CONFIG || '[]');
|
|
564
|
+
const site = sites.find((s) => s.id === id);
|
|
565
|
+
if (!site?.ga4_service_account_key) {
|
|
566
|
+
throw new Error(`GA4 not configured for site "${id}". Add ga4_service_account_key to WP_SITES_CONFIG.`);
|
|
567
|
+
}
|
|
568
|
+
const keyContent = JSON.parse(readFileSync(site.ga4_service_account_key, 'utf-8'));
|
|
569
|
+
const auth = new google.auth.GoogleAuth({
|
|
570
|
+
credentials: keyContent,
|
|
571
|
+
scopes: ['https://www.googleapis.com/auth/analytics.readonly'],
|
|
572
|
+
});
|
|
573
|
+
const authClient = await auth.getClient();
|
|
574
|
+
ga4AuthClients.set(id, authClient);
|
|
575
|
+
return authClient;
|
|
576
|
+
}
|
|
577
|
+
// ── Plausible Analytics Interface ────────────────────────────────
|
|
578
|
+
export function hasPlausible(siteId) {
|
|
579
|
+
const id = siteId || activeSiteId;
|
|
580
|
+
return plSiteClients.has(id);
|
|
581
|
+
}
|
|
582
|
+
export async function makePlausibleRequest(method, endpoint, params, siteId) {
|
|
583
|
+
const id = siteId || activeSiteId;
|
|
584
|
+
const client = plSiteClients.get(id);
|
|
585
|
+
if (!client) {
|
|
586
|
+
throw new Error(`Plausible not configured for site "${id}". Add plausible_api_key to WP_SITES_CONFIG.`);
|
|
587
|
+
}
|
|
588
|
+
const limiter = getLimiter(id);
|
|
589
|
+
await limiter.acquire();
|
|
590
|
+
try {
|
|
591
|
+
const response = await client.request({ method, url: endpoint, params });
|
|
592
|
+
return response.data;
|
|
593
|
+
}
|
|
594
|
+
finally {
|
|
595
|
+
limiter.release();
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
// ── Core Web Vitals Interface (Google API Key) ───────────────────
|
|
599
|
+
export function hasGoogleApiKey(siteId) {
|
|
600
|
+
const id = siteId || activeSiteId;
|
|
601
|
+
const sites = JSON.parse(process.env.WP_SITES_CONFIG || '[]');
|
|
602
|
+
const site = sites.find((s) => s.id === id);
|
|
603
|
+
return !!site?.google_api_key;
|
|
604
|
+
}
|
|
605
|
+
export function getGoogleApiKey(siteId) {
|
|
606
|
+
const id = siteId || activeSiteId;
|
|
607
|
+
const sites = JSON.parse(process.env.WP_SITES_CONFIG || '[]');
|
|
608
|
+
const site = sites.find((s) => s.id === id);
|
|
609
|
+
if (!site?.google_api_key) {
|
|
610
|
+
throw new Error(`Google API key not configured for site "${id}". Add google_api_key to WP_SITES_CONFIG.`);
|
|
611
|
+
}
|
|
612
|
+
return site.google_api_key;
|
|
613
|
+
}
|
|
614
|
+
// ── Slack Interface ─────────────────────────────────────────────
|
|
615
|
+
export function hasSlackWebhook(siteId) {
|
|
616
|
+
const id = siteId || activeSiteId;
|
|
617
|
+
const sites = JSON.parse(process.env.WP_SITES_CONFIG || '[]');
|
|
618
|
+
const site = sites.find((s) => s.id === id);
|
|
619
|
+
return !!site?.slack_webhook_url;
|
|
620
|
+
}
|
|
621
|
+
export function getSlackWebhookUrl(siteId) {
|
|
622
|
+
const id = siteId || activeSiteId;
|
|
623
|
+
const sites = JSON.parse(process.env.WP_SITES_CONFIG || '[]');
|
|
624
|
+
const site = sites.find((s) => s.id === id);
|
|
625
|
+
if (!site?.slack_webhook_url) {
|
|
626
|
+
throw new Error(`Slack webhook not configured for site "${id}". Add slack_webhook_url to WP_SITES_CONFIG.`);
|
|
627
|
+
}
|
|
628
|
+
return site.slack_webhook_url;
|
|
629
|
+
}
|
|
630
|
+
export function hasSlackBot(siteId) {
|
|
631
|
+
const id = siteId || activeSiteId;
|
|
632
|
+
return slackBotClients.has(id);
|
|
633
|
+
}
|
|
634
|
+
export async function makeSlackBotRequest(method, endpoint, data, siteId) {
|
|
635
|
+
const id = siteId || activeSiteId;
|
|
636
|
+
const client = slackBotClients.get(id);
|
|
637
|
+
if (!client) {
|
|
638
|
+
throw new Error(`Slack Bot not configured for site "${id}". Add slack_bot_token to WP_SITES_CONFIG.`);
|
|
639
|
+
}
|
|
640
|
+
const limiter = getLimiter(id);
|
|
641
|
+
await limiter.acquire();
|
|
642
|
+
try {
|
|
643
|
+
const response = await client.request({ method, url: endpoint, data });
|
|
644
|
+
return response.data;
|
|
645
|
+
}
|
|
646
|
+
finally {
|
|
647
|
+
limiter.release();
|
|
648
|
+
}
|
|
649
|
+
}
|
|
511
650
|
// ── Plugin Repository (External API) ────────────────────────────────
|
|
512
651
|
/**
|
|
513
652
|
* Search the WordPress.org Plugin Repository
|
|
@@ -4,7 +4,7 @@ description: “Use when the user asks about WordPress — whether development (
|
|
|
4
4
|
blocks, REST API) or operations (deploy, audit, backup, migrate, content management).
|
|
5
5
|
Classifies the task and routes to the correct development or operational skill/agent.”
|
|
6
6
|
compatibility: “Targets WordPress 6.9+ (PHP 7.2.24+). Filesystem-based agent with bash + node. Some workflows require WP-CLI.”
|
|
7
|
-
version: 1.
|
|
7
|
+
version: 1.4.0
|
|
8
8
|
source: “WordPress/agent-skills (GPL-2.0-or-later) + wordpress-manager extensions”
|
|
9
9
|
---
|
|
10
10
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Router decision tree (
|
|
1
|
+
# Router decision tree (v16 — development + local environment + operations + multisite + CI/CD + monitoring + webhooks + content repurposing + programmatic SEO + content attribution + multi-language network + social/email distribution + search console + content optimization + analytics + alerting + workflows)
|
|
2
2
|
|
|
3
3
|
This routing guide covers WordPress **development**, **local environment**, and **operations** workflows.
|
|
4
4
|
|
|
@@ -14,7 +14,7 @@ Keywords that indicate **local environment**:
|
|
|
14
14
|
local site, Studio, LocalWP, Local by Flywheel, wp-env, local WordPress, start site, stop site, create local site, local development, symlink plugin, local database, switch PHP version, localhost, local preview, detect environment, WASM, SQLite local
|
|
15
15
|
|
|
16
16
|
Keywords that indicate **operations**:
|
|
17
|
-
deploy, push to production, audit, security check, backup, restore, migrate, move site, create post, manage content, site status, check plugins, performance check, SEO audit, WooCommerce, prodotto, ordine, coupon, negozio, catalogo, inventario, vendite, carrello, multisite, network, sub-site, sub-sito, domain mapping, super admin, network activate, monitor, uptime, health report, trend, scansione periodica, alerting, performance baseline, fleet, all sites, network health, cross-site, webhook, outbound notification, event propagation, Zapier, content sync, repurpose content, social posts from blog, content atomization, newsletter from posts, content distribution, programmatic SEO, template pages, city pages, location pages, bulk page generation, scalable landing pages, content ROI, attribution, which content drives sales, conversion tracking, UTM tracking, revenue per post, multilingual, multi-language, hreflang, international SEO, translate site, language sites, localize content, social publish, schedule post, Buffer, email campaign, Mailchimp, SendGrid, transactional email, content distribution, newsletter send, Google Search Console, GSC, keyword tracking, keyword rankings, search analytics, indexing status, URL inspection, sitemap submit, search performance, SERP data, optimize content, headline scoring, readability analysis, SEO score, content scoring, meta optimization, content freshness, content triage, bulk optimize, Flesch-Kincaid, keyword density
|
|
17
|
+
deploy, push to production, audit, security check, backup, restore, migrate, move site, create post, manage content, site status, check plugins, performance check, SEO audit, WooCommerce, prodotto, ordine, coupon, negozio, catalogo, inventario, vendite, carrello, multisite, network, sub-site, sub-sito, domain mapping, super admin, network activate, monitor, uptime, health report, trend, scansione periodica, alerting, performance baseline, fleet, all sites, network health, cross-site, webhook, outbound notification, event propagation, Zapier, content sync, repurpose content, social posts from blog, content atomization, newsletter from posts, content distribution, programmatic SEO, template pages, city pages, location pages, bulk page generation, scalable landing pages, content ROI, attribution, which content drives sales, conversion tracking, UTM tracking, revenue per post, multilingual, multi-language, hreflang, international SEO, translate site, language sites, localize content, social publish, schedule post, Buffer, email campaign, Mailchimp, SendGrid, transactional email, content distribution, newsletter send, Google Search Console, GSC, keyword tracking, keyword rankings, search analytics, indexing status, URL inspection, sitemap submit, search performance, SERP data, optimize content, headline scoring, readability analysis, SEO score, content scoring, meta optimization, content freshness, content triage, bulk optimize, Flesch-Kincaid, keyword density, Google Analytics, GA4, traffic analytics, pageviews, sessions, user analytics, Plausible, privacy analytics, Core Web Vitals, CWV, LCP, INP, CLS, PageSpeed, page speed, site speed, performance score, Slack alert, email alert, notification channel, alert threshold, severity routing, escalation, incident notification, uptime alert, error alert, performance alert, scheduled report, health digest, alert cooldown, alert dedup, workflow trigger, automation, scheduled event, content lifecycle, cron trigger, hook trigger, workflow rule, automate, trigger management
|
|
18
18
|
|
|
19
19
|
Keywords that indicate **development**:
|
|
20
20
|
create block, block.json, theme.json, register_rest_route, plugin development, hooks, PHPStan, build, test, scaffold, i18n, translation, accessibility, a11y, headless, decoupled, WPGraphQL, CI, CD, pipeline, GitHub Actions, GitLab CI, deploy automatico, workflow, quality gate
|
|
@@ -110,6 +110,12 @@ Priority: `gutenberg` > `wp-core` > `wp-site` > `wp-block-theme` > `wp-block-plu
|
|
|
110
110
|
→ `wp-search-console` skill + `wp-content-strategist` agent
|
|
111
111
|
- **Content optimization / headline scoring / readability / SEO score / meta optimization / content freshness / content triage / optimize posts / Flesch-Kincaid / keyword density**
|
|
112
112
|
→ `wp-content-optimization` skill + `wp-content-strategist` agent
|
|
113
|
+
- **Google Analytics / GA4 / Plausible / traffic analytics / pageviews / sessions / user analytics / Core Web Vitals / CWV / PageSpeed / site speed / performance score**
|
|
114
|
+
→ `wp-analytics` skill + `wp-monitoring-agent` agent
|
|
115
|
+
- **Slack alert / email alert / notification channel / alert threshold / severity routing / escalation / incident notification / uptime alert / performance alert / scheduled report / health digest / alert cooldown**
|
|
116
|
+
→ `wp-alerting` skill + `wp-monitoring-agent` agent
|
|
117
|
+
- **Workflow trigger / automation / scheduled event / content lifecycle / cron trigger / hook trigger / workflow rule / automate / trigger management**
|
|
118
|
+
→ `wp-content-workflows` skill + `wp-site-manager` agent
|
|
113
119
|
|
|
114
120
|
## Step 2c: route by local environment intent (keywords)
|
|
115
121
|
|