claude-plugin-wordpress-manager 2.2.0 → 2.3.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 (34) hide show
  1. package/.claude-plugin/plugin.json +2 -2
  2. package/CHANGELOG.md +26 -0
  3. package/agents/wp-content-strategist.md +25 -0
  4. package/agents/wp-ecommerce-manager.md +23 -0
  5. package/agents/wp-site-manager.md +26 -0
  6. package/docs/GUIDE.md +116 -28
  7. package/package.json +8 -3
  8. package/skills/wordpress-router/references/decision-tree.md +8 -2
  9. package/skills/wp-content/SKILL.md +1 -0
  10. package/skills/wp-content-attribution/SKILL.md +97 -0
  11. package/skills/wp-content-attribution/references/attribution-models.md +189 -0
  12. package/skills/wp-content-attribution/references/conversion-funnels.md +137 -0
  13. package/skills/wp-content-attribution/references/reporting-dashboards.md +199 -0
  14. package/skills/wp-content-attribution/references/roi-calculation.md +202 -0
  15. package/skills/wp-content-attribution/references/utm-tracking-setup.md +161 -0
  16. package/skills/wp-content-attribution/scripts/attribution_inspect.mjs +277 -0
  17. package/skills/wp-headless/SKILL.md +1 -0
  18. package/skills/wp-i18n/SKILL.md +1 -0
  19. package/skills/wp-multilang-network/SKILL.md +107 -0
  20. package/skills/wp-multilang-network/references/content-sync.md +182 -0
  21. package/skills/wp-multilang-network/references/hreflang-config.md +198 -0
  22. package/skills/wp-multilang-network/references/language-routing.md +234 -0
  23. package/skills/wp-multilang-network/references/network-architecture.md +119 -0
  24. package/skills/wp-multilang-network/references/seo-international.md +213 -0
  25. package/skills/wp-multilang-network/scripts/multilang_inspect.mjs +308 -0
  26. package/skills/wp-multisite/SKILL.md +1 -0
  27. package/skills/wp-programmatic-seo/SKILL.md +97 -0
  28. package/skills/wp-programmatic-seo/references/data-sources.md +200 -0
  29. package/skills/wp-programmatic-seo/references/location-seo.md +134 -0
  30. package/skills/wp-programmatic-seo/references/product-seo.md +147 -0
  31. package/skills/wp-programmatic-seo/references/technical-seo.md +197 -0
  32. package/skills/wp-programmatic-seo/references/template-architecture.md +125 -0
  33. package/skills/wp-programmatic-seo/scripts/programmatic_seo_inspect.mjs +264 -0
  34. package/skills/wp-woocommerce/SKILL.md +1 -0
@@ -0,0 +1,264 @@
1
+ /**
2
+ * programmatic_seo_inspect.mjs — Detect programmatic SEO readiness.
3
+ *
4
+ * Scans for headless frontend, SEO plugins, content volume, custom post types,
5
+ * and WPGraphQL availability to assess readiness for programmatic page generation.
6
+ * Outputs a JSON report to stdout.
7
+ *
8
+ * Usage:
9
+ * node programmatic_seo_inspect.mjs [--cwd=/path/to/check]
10
+ *
11
+ * Exit codes:
12
+ * 0 — programmatic SEO indicators detected
13
+ * 1 — no programmatic SEO indicators detected
14
+ */
15
+
16
+ import fs from "node:fs";
17
+ import path from "node:path";
18
+ import process from "node:process";
19
+ import { execSync } from "node:child_process";
20
+
21
+ const TOOL_VERSION = "1.0.0";
22
+
23
+ // ---------------------------------------------------------------------------
24
+ // Helpers
25
+ // ---------------------------------------------------------------------------
26
+
27
+ function statSafe(p) {
28
+ try {
29
+ return fs.statSync(p);
30
+ } catch {
31
+ return null;
32
+ }
33
+ }
34
+
35
+ function readFileSafe(p) {
36
+ try {
37
+ return fs.readFileSync(p, "utf8");
38
+ } catch {
39
+ return null;
40
+ }
41
+ }
42
+
43
+ function readJsonSafe(p) {
44
+ const raw = readFileSafe(p);
45
+ if (!raw) return null;
46
+ try {
47
+ return JSON.parse(raw);
48
+ } catch {
49
+ return null;
50
+ }
51
+ }
52
+
53
+ function execSafe(cmd, cwd, timeoutMs = 5000) {
54
+ try {
55
+ return execSync(cmd, { encoding: "utf8", timeout: timeoutMs, cwd, stdio: ["pipe", "pipe", "pipe"] }).trim();
56
+ } catch {
57
+ return null;
58
+ }
59
+ }
60
+
61
+ function readdirSafe(dir) {
62
+ try {
63
+ return fs.readdirSync(dir);
64
+ } catch {
65
+ return [];
66
+ }
67
+ }
68
+
69
+ // ---------------------------------------------------------------------------
70
+ // Parse --cwd argument
71
+ // ---------------------------------------------------------------------------
72
+
73
+ function parseCwd() {
74
+ const cwdArg = process.argv.find((a) => a.startsWith("--cwd="));
75
+ return cwdArg ? cwdArg.slice(6) : process.cwd();
76
+ }
77
+
78
+ // ---------------------------------------------------------------------------
79
+ // Detect headless frontend
80
+ // ---------------------------------------------------------------------------
81
+
82
+ function detectHeadlessFrontend(cwd) {
83
+ const result = { detected: false, framework: null, location: null };
84
+
85
+ const frontendDirs = ["frontend", "client", "app", "web", "next", "nuxt", "."];
86
+ for (const dir of frontendDirs) {
87
+ const pkgPath = path.join(cwd, dir, "package.json");
88
+ const pkg = readJsonSafe(pkgPath);
89
+ if (!pkg) continue;
90
+ const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
91
+ if (allDeps["next"]) { result.detected = true; result.framework = "Next.js"; result.location = dir; return result; }
92
+ if (allDeps["nuxt"] || allDeps["nuxt3"]) { result.detected = true; result.framework = "Nuxt"; result.location = dir; return result; }
93
+ if (allDeps["astro"]) { result.detected = true; result.framework = "Astro"; result.location = dir; return result; }
94
+ if (allDeps["gatsby"]) { result.detected = true; result.framework = "Gatsby"; result.location = dir; return result; }
95
+ }
96
+
97
+ // Check for config files directly
98
+ const configFiles = [
99
+ { file: "next.config.js", fw: "Next.js" },
100
+ { file: "next.config.mjs", fw: "Next.js" },
101
+ { file: "next.config.ts", fw: "Next.js" },
102
+ { file: "nuxt.config.ts", fw: "Nuxt" },
103
+ { file: "nuxt.config.js", fw: "Nuxt" },
104
+ { file: "astro.config.mjs", fw: "Astro" },
105
+ { file: "astro.config.ts", fw: "Astro" },
106
+ ];
107
+ for (const { file, fw } of configFiles) {
108
+ if (statSafe(path.join(cwd, file))) {
109
+ result.detected = true;
110
+ result.framework = fw;
111
+ result.location = ".";
112
+ return result;
113
+ }
114
+ }
115
+
116
+ return result;
117
+ }
118
+
119
+ // ---------------------------------------------------------------------------
120
+ // Detect SEO plugins
121
+ // ---------------------------------------------------------------------------
122
+
123
+ function detectSeoPlugin(cwd) {
124
+ const result = { detected: false, plugin: null };
125
+
126
+ const pluginsDir = path.join(cwd, "wp-content", "plugins");
127
+ const seoPlugins = [
128
+ { dir: "wordpress-seo", name: "Yoast SEO" },
129
+ { dir: "seo-by-rank-math", name: "Rank Math" },
130
+ { dir: "all-in-one-seo-pack", name: "All in One SEO" },
131
+ { dir: "the-seo-framework-extension-manager", name: "The SEO Framework" },
132
+ ];
133
+
134
+ for (const plugin of seoPlugins) {
135
+ if (statSafe(path.join(pluginsDir, plugin.dir))?.isDirectory()) {
136
+ result.detected = true;
137
+ result.plugin = plugin.name;
138
+ return result;
139
+ }
140
+ }
141
+
142
+ return result;
143
+ }
144
+
145
+ // ---------------------------------------------------------------------------
146
+ // Detect content counts by type
147
+ // ---------------------------------------------------------------------------
148
+
149
+ function detectContentCounts(cwd) {
150
+ const result = { posts: 0, pages: 0, products: 0, custom_post_types: [] };
151
+
152
+ // Try WP-CLI for accurate counts
153
+ const postCount = execSafe("wp post list --post_type=post --post_status=publish --format=count 2>/dev/null", cwd);
154
+ if (postCount) result.posts = parseInt(postCount) || 0;
155
+
156
+ const pageCount = execSafe("wp post list --post_type=page --post_status=publish --format=count 2>/dev/null", cwd);
157
+ if (pageCount) result.pages = parseInt(pageCount) || 0;
158
+
159
+ const productCount = execSafe("wp post list --post_type=product --post_status=publish --format=count 2>/dev/null", cwd);
160
+ if (productCount) result.products = parseInt(productCount) || 0;
161
+
162
+ // Detect custom post types via WP-CLI
163
+ const cptList = execSafe("wp post-type list --_builtin=0 --format=json 2>/dev/null", cwd);
164
+ if (cptList) {
165
+ try {
166
+ const cpts = JSON.parse(cptList);
167
+ result.custom_post_types = cpts.map((c) => c.name || c);
168
+ } catch { /* ignore parse errors */ }
169
+ }
170
+
171
+ return result;
172
+ }
173
+
174
+ // ---------------------------------------------------------------------------
175
+ // Detect WPGraphQL
176
+ // ---------------------------------------------------------------------------
177
+
178
+ function detectWPGraphQL(cwd) {
179
+ const pluginsDir = path.join(cwd, "wp-content", "plugins");
180
+ if (statSafe(path.join(pluginsDir, "wp-graphql"))?.isDirectory()) return true;
181
+
182
+ const composer = readJsonSafe(path.join(cwd, "composer.json"));
183
+ if (composer) {
184
+ const allDeps = { ...composer.require, ...composer["require-dev"] };
185
+ if (allDeps["wp-graphql/wp-graphql"]) return true;
186
+ }
187
+
188
+ return false;
189
+ }
190
+
191
+ // ---------------------------------------------------------------------------
192
+ // Main
193
+ // ---------------------------------------------------------------------------
194
+
195
+ function main() {
196
+ const cwd = parseCwd();
197
+
198
+ if (!statSafe(cwd)?.isDirectory()) {
199
+ console.error(`Error: directory not found: ${cwd}`);
200
+ process.exit(1);
201
+ }
202
+
203
+ const headless = detectHeadlessFrontend(cwd);
204
+ const seo = detectSeoPlugin(cwd);
205
+ const content = detectContentCounts(cwd);
206
+ const hasWpgraphql = detectWPGraphQL(cwd);
207
+ const hasCustomPostTypes = content.custom_post_types.length > 0;
208
+
209
+ const detected = headless.detected || seo.detected || hasCustomPostTypes;
210
+
211
+ const report = {
212
+ tool: "programmatic_seo_inspect",
213
+ version: TOOL_VERSION,
214
+ cwd,
215
+ detected,
216
+ has_headless_frontend: headless.detected,
217
+ headless_framework: headless.framework,
218
+ headless_location: headless.location,
219
+ seo_plugin: seo.plugin,
220
+ content_counts: {
221
+ posts: content.posts,
222
+ pages: content.pages,
223
+ products: content.products,
224
+ },
225
+ has_custom_post_types: hasCustomPostTypes,
226
+ custom_post_types: content.custom_post_types,
227
+ has_wpgraphql: hasWpgraphql,
228
+ programmatic_seo_readiness: "unknown",
229
+ recommendations: [],
230
+ };
231
+
232
+ // Assess readiness
233
+ if (headless.detected && seo.detected && hasCustomPostTypes) {
234
+ report.programmatic_seo_readiness = "high";
235
+ } else if (headless.detected || (seo.detected && hasCustomPostTypes)) {
236
+ report.programmatic_seo_readiness = "medium";
237
+ } else if (seo.detected) {
238
+ report.programmatic_seo_readiness = "low";
239
+ } else {
240
+ report.programmatic_seo_readiness = "not_ready";
241
+ }
242
+
243
+ // Recommendations
244
+ if (!headless.detected) {
245
+ report.recommendations.push("No headless frontend detected. Install Next.js, Nuxt, or Astro for ISR/SSG page rendering.");
246
+ }
247
+ if (!seo.detected) {
248
+ report.recommendations.push("No SEO plugin detected. Install Yoast SEO or Rank Math for sitemap generation and meta management.");
249
+ }
250
+ if (!hasCustomPostTypes) {
251
+ report.recommendations.push("No custom post types found. Create CPTs to serve as structured data sources for programmatic pages.");
252
+ }
253
+ if (!hasWpgraphql && headless.detected) {
254
+ report.recommendations.push("WPGraphQL not installed. Consider it for efficient batch data fetching from headless frontend.");
255
+ }
256
+ if (content.posts === 0 && content.pages === 0 && content.products === 0) {
257
+ report.recommendations.push("No published content found. Create initial content or verify WP-CLI access for accurate counts.");
258
+ }
259
+
260
+ console.log(JSON.stringify(report, null, 2));
261
+ process.exit(detected ? 0 : 1);
262
+ }
263
+
264
+ main();
@@ -109,3 +109,4 @@ For complex multi-step WooCommerce operations, use the `wp-ecommerce-manager` ag
109
109
  - `wp-audit` — Audit WooCommerce store security and performance
110
110
  - `wp-backup` — Backup WooCommerce database and uploads
111
111
  - `wp-webhooks` — WooCommerce webhook management (order/product/customer event notifications)
112
+ - `wp-content-attribution` — Content-commerce attribution, UTM tracking, revenue per content piece