claude-plugin-wordpress-manager 1.4.0 → 1.7.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 +7 -3
- package/CHANGELOG.md +111 -0
- package/README.md +10 -3
- package/agents/wp-accessibility-auditor.md +206 -0
- package/agents/wp-content-strategist.md +18 -0
- package/agents/wp-deployment-engineer.md +34 -2
- package/agents/wp-performance-optimizer.md +12 -0
- package/agents/wp-security-auditor.md +20 -0
- package/agents/wp-security-hardener.md +266 -0
- package/agents/wp-site-manager.md +14 -0
- package/agents/wp-test-engineer.md +207 -0
- package/docs/GUIDE.md +68 -15
- package/docs/guides/INDEX.md +46 -0
- package/docs/guides/wp-blog.md +590 -0
- package/docs/guides/wp-design-system.md +976 -0
- package/docs/guides/wp-ecommerce.md +786 -0
- package/docs/guides/wp-landing-page.md +762 -0
- package/docs/guides/wp-portfolio.md +713 -0
- package/docs/plans/2026-02-27-design-system-guide-design.md +30 -0
- package/docs/plans/2026-02-27-local-dev-tools-assessment.md +332 -0
- package/docs/plans/2026-02-27-local-env-design.md +179 -0
- package/docs/plans/2026-02-27-site-type-guides-design.md +44 -0
- package/package.json +7 -3
- package/skills/wordpress-router/SKILL.md +25 -5
- package/skills/wordpress-router/references/decision-tree.md +59 -3
- package/skills/wp-accessibility/SKILL.md +170 -0
- package/skills/wp-accessibility/references/a11y-audit-tools.md +248 -0
- package/skills/wp-accessibility/references/a11y-testing.md +222 -0
- package/skills/wp-accessibility/references/block-a11y.md +247 -0
- package/skills/wp-accessibility/references/interactive-a11y.md +272 -0
- package/skills/wp-accessibility/references/media-a11y.md +254 -0
- package/skills/wp-accessibility/references/theme-a11y.md +309 -0
- package/skills/wp-audit/SKILL.md +4 -0
- package/skills/wp-block-development/SKILL.md +5 -0
- package/skills/wp-block-themes/SKILL.md +4 -0
- package/skills/wp-deploy/SKILL.md +12 -0
- package/skills/wp-e2e-testing/SKILL.md +186 -0
- package/skills/wp-e2e-testing/references/ci-integration.md +174 -0
- package/skills/wp-e2e-testing/references/jest-wordpress.md +114 -0
- package/skills/wp-e2e-testing/references/phpunit-wordpress.md +141 -0
- package/skills/wp-e2e-testing/references/playwright-wordpress.md +108 -0
- package/skills/wp-e2e-testing/references/test-data-generation.md +127 -0
- package/skills/wp-e2e-testing/references/visual-regression.md +107 -0
- package/skills/wp-e2e-testing/references/wp-env-setup.md +97 -0
- package/skills/wp-e2e-testing/scripts/test_inspect.mjs +375 -0
- package/skills/wp-headless/SKILL.md +168 -0
- package/skills/wp-headless/references/api-layer-choice.md +160 -0
- package/skills/wp-headless/references/cors-config.md +245 -0
- package/skills/wp-headless/references/frontend-integration.md +331 -0
- package/skills/wp-headless/references/headless-auth.md +286 -0
- package/skills/wp-headless/references/webhooks.md +277 -0
- package/skills/wp-headless/references/wpgraphql.md +331 -0
- package/skills/wp-headless/scripts/headless_inspect.mjs +321 -0
- package/skills/wp-i18n/SKILL.md +170 -0
- package/skills/wp-i18n/references/js-i18n.md +201 -0
- package/skills/wp-i18n/references/multilingual-setup.md +219 -0
- package/skills/wp-i18n/references/php-i18n.md +196 -0
- package/skills/wp-i18n/references/rtl-support.md +206 -0
- package/skills/wp-i18n/references/translation-workflow.md +178 -0
- package/skills/wp-i18n/references/wpcli-i18n.md +177 -0
- package/skills/wp-i18n/scripts/i18n_inspect.mjs +330 -0
- package/skills/wp-interactivity-api/SKILL.md +4 -0
- package/skills/wp-local-env/SKILL.md +233 -0
- package/skills/wp-local-env/references/localwp-adapter.md +156 -0
- package/skills/wp-local-env/references/mcp-adapter-setup.md +153 -0
- package/skills/wp-local-env/references/studio-adapter.md +127 -0
- package/skills/wp-local-env/references/wpenv-adapter.md +121 -0
- package/skills/wp-local-env/scripts/detect_local_env.mjs +404 -0
- package/skills/wp-playground/SKILL.md +13 -1
- package/skills/wp-plugin-development/SKILL.md +6 -0
- package/skills/wp-rest-api/SKILL.md +4 -0
- package/skills/wp-security/SKILL.md +179 -0
- package/skills/wp-security/references/api-restriction.md +147 -0
- package/skills/wp-security/references/authentication-hardening.md +105 -0
- package/skills/wp-security/references/filesystem-hardening.md +105 -0
- package/skills/wp-security/references/http-headers.md +105 -0
- package/skills/wp-security/references/incident-response.md +144 -0
- package/skills/wp-security/references/user-capabilities.md +115 -0
- package/skills/wp-security/references/wp-config-security.md +129 -0
- package/skills/wp-security/scripts/security_inspect.mjs +393 -0
- package/skills/wp-wpcli-and-ops/SKILL.md +6 -0
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# wp-env Adapter
|
|
2
|
+
|
|
3
|
+
## Prerequisites
|
|
4
|
+
|
|
5
|
+
- **Docker** installed and running (`docker info`)
|
|
6
|
+
- **Node.js** 18+ and npm
|
|
7
|
+
- `.wp-env.json` in the project root
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm -g install @wordpress/env
|
|
13
|
+
# or use npx (no install):
|
|
14
|
+
npx @wordpress/env start
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Configuration (.wp-env.json)
|
|
18
|
+
|
|
19
|
+
Minimal:
|
|
20
|
+
```json
|
|
21
|
+
{
|
|
22
|
+
"core": null,
|
|
23
|
+
"plugins": ["./my-plugin"],
|
|
24
|
+
"themes": ["./my-theme"]
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Full example:
|
|
29
|
+
```json
|
|
30
|
+
{
|
|
31
|
+
"core": "WordPress/WordPress#6.9",
|
|
32
|
+
"phpVersion": "8.2",
|
|
33
|
+
"plugins": [
|
|
34
|
+
"./my-plugin",
|
|
35
|
+
"https://downloads.wordpress.org/plugin/woocommerce.latest-stable.zip"
|
|
36
|
+
],
|
|
37
|
+
"themes": ["./my-theme"],
|
|
38
|
+
"port": 8888,
|
|
39
|
+
"testsPort": 8889,
|
|
40
|
+
"config": {
|
|
41
|
+
"WP_DEBUG": true,
|
|
42
|
+
"WP_DEBUG_LOG": true,
|
|
43
|
+
"SCRIPT_DEBUG": true
|
|
44
|
+
},
|
|
45
|
+
"mappings": {
|
|
46
|
+
"wp-content/mu-plugins": "./mu-plugins"
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Override per-developer settings in `.wp-env.override.json` (gitignored).
|
|
52
|
+
|
|
53
|
+
## Commands
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
# Start development and test environments
|
|
57
|
+
npx wp-env start
|
|
58
|
+
npx wp-env start --update # Also pull latest images
|
|
59
|
+
|
|
60
|
+
# Stop environments
|
|
61
|
+
npx wp-env stop
|
|
62
|
+
|
|
63
|
+
# Destroy (remove containers and data)
|
|
64
|
+
npx wp-env destroy
|
|
65
|
+
|
|
66
|
+
# Run WP-CLI commands
|
|
67
|
+
npx wp-env run cli wp <command>
|
|
68
|
+
npx wp-env run cli wp plugin list
|
|
69
|
+
npx wp-env run cli wp scaffold block my-block --plugin=my-plugin
|
|
70
|
+
npx wp-env run cli wp db export /tmp/backup.sql
|
|
71
|
+
|
|
72
|
+
# Run arbitrary commands in containers
|
|
73
|
+
npx wp-env run cli bash
|
|
74
|
+
npx wp-env run tests-cli wp test
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Ports
|
|
78
|
+
|
|
79
|
+
| Environment | Default port |
|
|
80
|
+
|-------------|-------------|
|
|
81
|
+
| Development | `http://localhost:8888` |
|
|
82
|
+
| Tests | `http://localhost:8889` |
|
|
83
|
+
|
|
84
|
+
Configurable via `port` and `testsPort` in `.wp-env.json`.
|
|
85
|
+
|
|
86
|
+
## Default credentials
|
|
87
|
+
|
|
88
|
+
- **User**: `admin`
|
|
89
|
+
- **Password**: `password`
|
|
90
|
+
- **Database**: Inside Docker container (not directly accessible from host)
|
|
91
|
+
|
|
92
|
+
## REST API
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
curl http://localhost:8888/wp-json/wp/v2/posts
|
|
96
|
+
curl -u "admin:password" http://localhost:8888/wp-json/wp/v2/posts
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## When to use wp-env
|
|
100
|
+
|
|
101
|
+
- **WordPress core contribution** — standard for Gutenberg development
|
|
102
|
+
- **CI/CD pipelines** — Docker-based, reproducible
|
|
103
|
+
- **Plugin/theme testing** — isolated, disposable
|
|
104
|
+
- **PHPUnit tests** — `npx wp-env run tests-cli --env-cwd=wp-content/plugins/my-plugin phpunit`
|
|
105
|
+
|
|
106
|
+
## When NOT to use wp-env
|
|
107
|
+
|
|
108
|
+
- Quick theme previews → use WordPress Studio instead
|
|
109
|
+
- Production-parity with specific hosting → use LocalWP
|
|
110
|
+
- No Docker available → use WordPress Studio (WASM-based)
|
|
111
|
+
|
|
112
|
+
## Comparison with Studio and LocalWP
|
|
113
|
+
|
|
114
|
+
| Feature | wp-env | Studio | LocalWP |
|
|
115
|
+
|---------|--------|--------|---------|
|
|
116
|
+
| Requires Docker | Yes | No | No |
|
|
117
|
+
| Config as code | `.wp-env.json` | CLI flags | GUI |
|
|
118
|
+
| CI/CD friendly | Excellent | Limited | No |
|
|
119
|
+
| Setup speed | Medium (Docker pull) | Fast (WASM) | Slow (native) |
|
|
120
|
+
| Multiple sites | 2 (dev + test) | Unlimited | Unlimited |
|
|
121
|
+
| Multisite | Yes | No | Yes |
|
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* detect_local_env.mjs — Detect local WordPress development environments.
|
|
3
|
+
*
|
|
4
|
+
* Scans for WordPress Studio, LocalWP, and wp-env installations.
|
|
5
|
+
* Outputs a JSON report to stdout with discovered environments, sites,
|
|
6
|
+
* recommended tool, and WP-CLI invocation paths.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* node detect_local_env.mjs [--cwd=/path/to/check]
|
|
10
|
+
*
|
|
11
|
+
* Exit codes:
|
|
12
|
+
* 0 — at least one environment found
|
|
13
|
+
* 1 — no environments found
|
|
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
|
+
import os from "node:os";
|
|
21
|
+
|
|
22
|
+
const TOOL_VERSION = "1.0.0";
|
|
23
|
+
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
// Helpers
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
|
|
28
|
+
function statSafe(p) {
|
|
29
|
+
try {
|
|
30
|
+
return fs.statSync(p);
|
|
31
|
+
} catch {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function readFileSafe(p) {
|
|
37
|
+
try {
|
|
38
|
+
return fs.readFileSync(p, "utf8");
|
|
39
|
+
} catch {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function readJsonSafe(p) {
|
|
45
|
+
const raw = readFileSafe(p);
|
|
46
|
+
if (!raw) return null;
|
|
47
|
+
try {
|
|
48
|
+
return JSON.parse(raw);
|
|
49
|
+
} catch {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function execSafe(cmd, timeoutMs = 5000) {
|
|
55
|
+
try {
|
|
56
|
+
return execSync(cmd, { encoding: "utf8", timeout: timeoutMs, stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
57
|
+
} catch {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function whichSafe(bin) {
|
|
63
|
+
const cmd = process.platform === "win32" ? `where ${bin}` : `command -v ${bin}`;
|
|
64
|
+
return execSafe(cmd);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// ---------------------------------------------------------------------------
|
|
68
|
+
// Platform paths
|
|
69
|
+
// ---------------------------------------------------------------------------
|
|
70
|
+
|
|
71
|
+
function getPlatformPaths() {
|
|
72
|
+
const home = os.homedir();
|
|
73
|
+
const platform = process.platform;
|
|
74
|
+
|
|
75
|
+
if (platform === "darwin") {
|
|
76
|
+
return {
|
|
77
|
+
studio: {
|
|
78
|
+
sites: path.join(home, "Studio"),
|
|
79
|
+
config: path.join(home, "Library", "Application Support", "WordPressStudio"),
|
|
80
|
+
},
|
|
81
|
+
localwp: {
|
|
82
|
+
sites: path.join(home, "Local Sites"),
|
|
83
|
+
config: path.join(home, "Library", "Application Support", "Local"),
|
|
84
|
+
json: path.join(home, "Library", "Application Support", "Local", "sites.json"),
|
|
85
|
+
lightning: path.join(home, "Library", "Application Support", "Local", "lightning-services"),
|
|
86
|
+
run: path.join(home, "Library", "Application Support", "Local", "run"),
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (platform === "win32") {
|
|
92
|
+
const appData = process.env.APPDATA || path.join(home, "AppData", "Roaming");
|
|
93
|
+
const localAppData = process.env.LOCALAPPDATA || path.join(home, "AppData", "Local");
|
|
94
|
+
return {
|
|
95
|
+
studio: {
|
|
96
|
+
sites: path.join(home, "Studio"),
|
|
97
|
+
config: path.join(localAppData, "WordPressStudio"),
|
|
98
|
+
},
|
|
99
|
+
localwp: {
|
|
100
|
+
sites: path.join(home, "Local Sites"),
|
|
101
|
+
config: path.join(appData, "Local"),
|
|
102
|
+
json: path.join(appData, "Local", "sites.json"),
|
|
103
|
+
lightning: path.join(appData, "Local", "lightning-services"),
|
|
104
|
+
run: path.join(appData, "Local", "run"),
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Linux (default)
|
|
110
|
+
return {
|
|
111
|
+
studio: {
|
|
112
|
+
sites: path.join(home, "Studio"),
|
|
113
|
+
config: path.join(home, ".config", "WordPressStudio"),
|
|
114
|
+
},
|
|
115
|
+
localwp: {
|
|
116
|
+
sites: path.join(home, "Local Sites"),
|
|
117
|
+
config: path.join(home, ".config", "Local"),
|
|
118
|
+
json: path.join(home, ".config", "Local", "sites.json"),
|
|
119
|
+
lightning: path.join(home, ".config", "Local", "lightning-services"),
|
|
120
|
+
run: path.join(home, ".config", "Local", "run"),
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// ---------------------------------------------------------------------------
|
|
126
|
+
// WordPress Studio detection
|
|
127
|
+
// ---------------------------------------------------------------------------
|
|
128
|
+
|
|
129
|
+
function detectStudio(paths) {
|
|
130
|
+
const cli = whichSafe("studio");
|
|
131
|
+
const sitesDir = paths.studio.sites;
|
|
132
|
+
const hasSitesDir = statSafe(sitesDir)?.isDirectory();
|
|
133
|
+
|
|
134
|
+
if (!cli && !hasSitesDir) return null;
|
|
135
|
+
|
|
136
|
+
// Get version from CLI
|
|
137
|
+
let version = null;
|
|
138
|
+
if (cli) {
|
|
139
|
+
const vOut = execSafe("studio --version");
|
|
140
|
+
if (vOut) version = vOut.replace(/[^0-9.]/g, "") || vOut;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Discover sites
|
|
144
|
+
const sites = [];
|
|
145
|
+
if (hasSitesDir) {
|
|
146
|
+
try {
|
|
147
|
+
const entries = fs.readdirSync(sitesDir, { withFileTypes: true });
|
|
148
|
+
for (const entry of entries) {
|
|
149
|
+
if (!entry.isDirectory()) continue;
|
|
150
|
+
const sitePath = path.join(sitesDir, entry.name);
|
|
151
|
+
const wpConfig = path.join(sitePath, "wp-config.php");
|
|
152
|
+
if (!statSafe(wpConfig)) continue;
|
|
153
|
+
|
|
154
|
+
const site = {
|
|
155
|
+
name: entry.name,
|
|
156
|
+
path: sitePath,
|
|
157
|
+
url: null,
|
|
158
|
+
status: "unknown",
|
|
159
|
+
php: null,
|
|
160
|
+
wp: null,
|
|
161
|
+
db: "sqlite",
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
// Check if SQLite db exists
|
|
165
|
+
const sqliteDb = path.join(sitePath, "wp-content", "database", ".ht.sqlite");
|
|
166
|
+
if (statSafe(sqliteDb)) {
|
|
167
|
+
site.db = "sqlite";
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Try to get site info via CLI
|
|
171
|
+
if (cli) {
|
|
172
|
+
const statusOut = execSafe(`studio site status --path="${sitePath}"`);
|
|
173
|
+
if (statusOut) {
|
|
174
|
+
const urlMatch = statusOut.match(/URL:\s*(https?:\/\/\S+)/i);
|
|
175
|
+
if (urlMatch) site.url = urlMatch[1];
|
|
176
|
+
const phpMatch = statusOut.match(/PHP:\s*(\d+\.\d+)/i);
|
|
177
|
+
if (phpMatch) site.php = phpMatch[1];
|
|
178
|
+
const wpMatch = statusOut.match(/WordPress:\s*(\d+\.\d+(?:\.\d+)?)/i);
|
|
179
|
+
if (wpMatch) site.wp = wpMatch[1];
|
|
180
|
+
if (/running/i.test(statusOut)) site.status = "running";
|
|
181
|
+
else if (/stopped/i.test(statusOut)) site.status = "stopped";
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
sites.push(site);
|
|
186
|
+
}
|
|
187
|
+
} catch {
|
|
188
|
+
// Permission or read error — skip
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return {
|
|
193
|
+
tool: "studio",
|
|
194
|
+
version,
|
|
195
|
+
cli: cli || null,
|
|
196
|
+
sites,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// ---------------------------------------------------------------------------
|
|
201
|
+
// LocalWP detection
|
|
202
|
+
// ---------------------------------------------------------------------------
|
|
203
|
+
|
|
204
|
+
function detectLocalWP(paths) {
|
|
205
|
+
const sitesJson = paths.localwp.json;
|
|
206
|
+
const sitesData = readJsonSafe(sitesJson);
|
|
207
|
+
const hasSitesDir = statSafe(paths.localwp.sites)?.isDirectory();
|
|
208
|
+
|
|
209
|
+
if (!sitesData && !hasSitesDir) return null;
|
|
210
|
+
|
|
211
|
+
// Find wp-cli binary
|
|
212
|
+
let wpCliBin = null;
|
|
213
|
+
const lightningDir = paths.localwp.lightning;
|
|
214
|
+
if (statSafe(lightningDir)?.isDirectory()) {
|
|
215
|
+
try {
|
|
216
|
+
const entries = fs.readdirSync(lightningDir);
|
|
217
|
+
const wcliDir = entries.find((e) => e.startsWith("wp-cli"));
|
|
218
|
+
if (wcliDir) {
|
|
219
|
+
const candidate = path.join(lightningDir, wcliDir, "bin", "wp");
|
|
220
|
+
if (statSafe(candidate)) wpCliBin = candidate;
|
|
221
|
+
}
|
|
222
|
+
} catch {
|
|
223
|
+
// Skip
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Parse sites from sites.json
|
|
228
|
+
const sites = [];
|
|
229
|
+
if (sitesData && typeof sitesData === "object") {
|
|
230
|
+
const siteEntries = Array.isArray(sitesData) ? sitesData : Object.values(sitesData);
|
|
231
|
+
for (const s of siteEntries) {
|
|
232
|
+
if (!s || typeof s !== "object") continue;
|
|
233
|
+
|
|
234
|
+
const sitePath = s.path
|
|
235
|
+
? path.join(s.path, "app", "public")
|
|
236
|
+
: s.name
|
|
237
|
+
? path.join(paths.localwp.sites, s.name, "app", "public")
|
|
238
|
+
: null;
|
|
239
|
+
|
|
240
|
+
const site = {
|
|
241
|
+
name: s.name || s.domain || "unknown",
|
|
242
|
+
path: sitePath,
|
|
243
|
+
url: s.url || (s.domain ? `http://${s.domain}` : null),
|
|
244
|
+
port: s.services?.nginx?.port || s.services?.apache?.port || null,
|
|
245
|
+
status: "unknown",
|
|
246
|
+
php: s.services?.php?.version || null,
|
|
247
|
+
wp: null,
|
|
248
|
+
db: "mysql",
|
|
249
|
+
webServer: s.services?.nginx ? "nginx" : s.services?.apache ? "apache" : null,
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
// Check if site directory exists
|
|
253
|
+
if (sitePath && statSafe(sitePath)?.isDirectory()) {
|
|
254
|
+
site.status = "stopped"; // Directory exists but we can't confirm running
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Check MySQL socket to determine if running
|
|
258
|
+
if (s.id && paths.localwp.run) {
|
|
259
|
+
const socketPath = path.join(paths.localwp.run, s.id, "mysql", "mysqld.sock");
|
|
260
|
+
if (statSafe(socketPath)) {
|
|
261
|
+
site.status = "running";
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
sites.push(site);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Get LocalWP version
|
|
270
|
+
let version = null;
|
|
271
|
+
const pkgJson = readJsonSafe(path.join(paths.localwp.config, "package.json"));
|
|
272
|
+
if (pkgJson?.version) version = pkgJson.version;
|
|
273
|
+
|
|
274
|
+
return {
|
|
275
|
+
tool: "localwp",
|
|
276
|
+
version,
|
|
277
|
+
configPath: sitesJson,
|
|
278
|
+
wpCliBin,
|
|
279
|
+
sites,
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// ---------------------------------------------------------------------------
|
|
284
|
+
// wp-env detection
|
|
285
|
+
// ---------------------------------------------------------------------------
|
|
286
|
+
|
|
287
|
+
function detectWpEnv(cwd) {
|
|
288
|
+
const wpEnvJson = path.join(cwd, ".wp-env.json");
|
|
289
|
+
const wpEnvOverride = path.join(cwd, ".wp-env.override.json");
|
|
290
|
+
|
|
291
|
+
const config = readJsonSafe(wpEnvJson);
|
|
292
|
+
if (!config) return null;
|
|
293
|
+
|
|
294
|
+
const overrides = readJsonSafe(wpEnvOverride);
|
|
295
|
+
const merged = overrides ? { ...config, ...overrides } : config;
|
|
296
|
+
|
|
297
|
+
const devPort = merged.port || 8888;
|
|
298
|
+
const testPort = merged.testsPort || 8889;
|
|
299
|
+
|
|
300
|
+
return {
|
|
301
|
+
tool: "wpenv",
|
|
302
|
+
version: null,
|
|
303
|
+
configPath: wpEnvJson,
|
|
304
|
+
sites: [
|
|
305
|
+
{
|
|
306
|
+
name: "development",
|
|
307
|
+
path: cwd,
|
|
308
|
+
url: `http://localhost:${devPort}`,
|
|
309
|
+
port: devPort,
|
|
310
|
+
status: "unknown",
|
|
311
|
+
php: merged.phpVersion || null,
|
|
312
|
+
wp: merged.core || null,
|
|
313
|
+
db: "mysql",
|
|
314
|
+
},
|
|
315
|
+
{
|
|
316
|
+
name: "tests",
|
|
317
|
+
path: cwd,
|
|
318
|
+
url: `http://localhost:${testPort}`,
|
|
319
|
+
port: testPort,
|
|
320
|
+
status: "unknown",
|
|
321
|
+
php: merged.phpVersion || null,
|
|
322
|
+
wp: merged.core || null,
|
|
323
|
+
db: "mysql",
|
|
324
|
+
},
|
|
325
|
+
],
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// ---------------------------------------------------------------------------
|
|
330
|
+
// Recommendation logic
|
|
331
|
+
// ---------------------------------------------------------------------------
|
|
332
|
+
|
|
333
|
+
function recommend(environments) {
|
|
334
|
+
if (environments.length === 0) return null;
|
|
335
|
+
if (environments.length === 1) return environments[0].tool;
|
|
336
|
+
|
|
337
|
+
// Prefer Studio if CLI is available (best automation surface)
|
|
338
|
+
const studio = environments.find((e) => e.tool === "studio" && e.cli);
|
|
339
|
+
if (studio) return "studio";
|
|
340
|
+
|
|
341
|
+
// Then LocalWP if it has sites
|
|
342
|
+
const localwp = environments.find((e) => e.tool === "localwp" && e.sites.length > 0);
|
|
343
|
+
if (localwp) return "localwp";
|
|
344
|
+
|
|
345
|
+
// Then wp-env
|
|
346
|
+
const wpenv = environments.find((e) => e.tool === "wpenv");
|
|
347
|
+
if (wpenv) return "wpenv";
|
|
348
|
+
|
|
349
|
+
return environments[0].tool;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
function buildWpCliMap(environments) {
|
|
353
|
+
const map = {};
|
|
354
|
+
for (const env of environments) {
|
|
355
|
+
if (env.tool === "studio" && env.cli) {
|
|
356
|
+
map.studio = "studio wp --path=<site>";
|
|
357
|
+
}
|
|
358
|
+
if (env.tool === "localwp" && env.wpCliBin) {
|
|
359
|
+
map.localwp = `${env.wpCliBin} --path=<site>`;
|
|
360
|
+
}
|
|
361
|
+
if (env.tool === "wpenv") {
|
|
362
|
+
map.wpenv = "npx wp-env run cli wp";
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
return map;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// ---------------------------------------------------------------------------
|
|
369
|
+
// Main
|
|
370
|
+
// ---------------------------------------------------------------------------
|
|
371
|
+
|
|
372
|
+
function main() {
|
|
373
|
+
const args = process.argv.slice(2);
|
|
374
|
+
let cwd = process.cwd();
|
|
375
|
+
for (const arg of args) {
|
|
376
|
+
if (arg.startsWith("--cwd=")) cwd = arg.slice(6);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
const paths = getPlatformPaths();
|
|
380
|
+
const environments = [];
|
|
381
|
+
|
|
382
|
+
const studio = detectStudio(paths);
|
|
383
|
+
if (studio) environments.push(studio);
|
|
384
|
+
|
|
385
|
+
const localwp = detectLocalWP(paths);
|
|
386
|
+
if (localwp) environments.push(localwp);
|
|
387
|
+
|
|
388
|
+
const wpenv = detectWpEnv(cwd);
|
|
389
|
+
if (wpenv) environments.push(wpenv);
|
|
390
|
+
|
|
391
|
+
const report = {
|
|
392
|
+
version: TOOL_VERSION,
|
|
393
|
+
timestamp: new Date().toISOString(),
|
|
394
|
+
platform: process.platform,
|
|
395
|
+
environments,
|
|
396
|
+
recommended: recommend(environments),
|
|
397
|
+
wpCli: buildWpCliMap(environments),
|
|
398
|
+
};
|
|
399
|
+
|
|
400
|
+
process.stdout.write(JSON.stringify(report, null, 2) + "\n");
|
|
401
|
+
process.exit(environments.length > 0 ? 0 : 1);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
main();
|
|
@@ -99,5 +99,17 @@ npx @wp-playground/cli@latest build-snapshot --blueprint=<file> --outfile=./site
|
|
|
99
99
|
|
|
100
100
|
## Escalation
|
|
101
101
|
|
|
102
|
-
- If PHP extensions or native DB access are required, Playground may be unsuitable; fall back to
|
|
102
|
+
- If PHP extensions or native DB access are required, Playground may be unsuitable; fall back to a local dev environment.
|
|
103
|
+
- For persistent local development, use the `wp-local-env` skill which supports WordPress Studio (WASM-based, lightweight), LocalWP (native MySQL, production-parity), and wp-env (Docker, CI-friendly).
|
|
103
104
|
- For browser-only embedding or VS Code extension specifics, consult the upstream docs: https://wordpress.github.io/wordpress-playground/
|
|
105
|
+
|
|
106
|
+
## Relationship with wp-local-env
|
|
107
|
+
|
|
108
|
+
| Need | Use |
|
|
109
|
+
|------|-----|
|
|
110
|
+
| Quick disposable test | wp-playground (this skill) |
|
|
111
|
+
| Persistent local dev with DB | `wp-local-env` (Studio/LocalWP/wp-env) |
|
|
112
|
+
| CI/CD testing | wp-playground (blueprints) or wp-env |
|
|
113
|
+
| Plugin/theme development | `wp-local-env` (symlink workflow) |
|
|
114
|
+
| Version switching (ephemeral) | wp-playground (`--wp=` / `--php=`) |
|
|
115
|
+
| Version switching (persistent) | `wp-local-env` (tool-specific settings) |
|
|
@@ -112,3 +112,9 @@ See:
|
|
|
112
112
|
## Escalation
|
|
113
113
|
|
|
114
114
|
For canonical detail, consult the Plugin Handbook and security guidelines before inventing patterns.
|
|
115
|
+
|
|
116
|
+
## Related skills
|
|
117
|
+
|
|
118
|
+
- `wp-e2e-testing` — PHPUnit for plugin unit tests, Playwright for E2E, CI integration
|
|
119
|
+
- `wp-i18n` — Text domain setup, translation workflow, WP-CLI i18n commands
|
|
120
|
+
- `wp-security` — Filesystem hardening, wp-config constants, authentication security
|
|
@@ -114,3 +114,7 @@ Read `references/discovery-and-params.md`.
|
|
|
114
114
|
## Escalation
|
|
115
115
|
|
|
116
116
|
If version support or behavior is unclear, consult the REST API Handbook and core docs before inventing patterns.
|
|
117
|
+
|
|
118
|
+
## Related skills
|
|
119
|
+
|
|
120
|
+
- `wp-headless` — Decoupled architecture, WPGraphQL, CORS configuration, frontend framework integration, webhooks
|