wp-lab
WordPress environment automation for local development.
Mode routing
Choose the mode before launching anything:
| Mode | Engine | Use for | Do not use for |
|---|---|---|---|
ephemeral |
wp-now from a temp or wrapper dir | Fast plugin/theme exploration, throwaway repros, short-lived WordPress playgrounds | Client builds that need MySQL or staging parity |
continuous |
wp-env Docker | Multi-plugin stacks, Gutenberg core dev, Docker-backed persistence | Windows client child-theme work when Laragon is available |
persistent-client |
Laragon, Apache, MySQL | Net-new client child theme builds, staging parity, local dev for a named client | Throwaway plugin checks |
If the user says "new client build", "client child theme dev", "set up local dev for X", "Laragon", or needs real MySQL, route to persistent-client. If they only need to see what a plugin or theme does, route to ephemeral. If they specifically need Docker, Gutenberg core, or a complex multi-plugin fixture, route to continuous.
Modes
ephemeral (wp-now)
- Starts in ~5 seconds, no Docker required
- Uses
@wp-now/wp-now(native PHP binaries, SQLite) - Spawned from
/tmp/wp-lab/...— DB dies with the dir - Can also run from a stable project wrapper when a longer-lived wp-now preview is enough
- Use for: quick code exploration, plugin testing, "what does this do?"
continuous (wp-env, Docker)
- Docker-backed, persistent volumes, multi-plugin stacks
- Config lives in
.wp-env.jsonin the project directory - Use for: complex multi-plugin staging mirrors, Gutenberg core dev, anything needing a real MySQL + nginx that wp-now's SQLite/PHP-server can't fake.
persistent-client (Laragon)
- Windows Laragon setup with Apache + MySQL, matching the SpinupWP staging layout
- Uses the client child theme from
F:\dev\voyager-clients\<client-slug> - Symlinks the child theme into
F:\laragon\www\<client-slug>\wp-content\themes\<client-slug> - Installs the Voyager parent theme,
voyager-core,voyager-blocks,voyager-orbit, and required plugins from the WP Plugins DB - Use for: net-new Voyager client builds, real client child theme dev, local reproduction of staging-only MySQL issues.
Prerequisites
Check before launching:
- Ephemeral: Node.js 18+ (no Docker needed)
- Continuous: Node.js 18+ AND Docker running
- Persistent client: Windows 10/11, Laragon Full, Apache + MySQL running, GitHub CLI authenticated to
voyager-marketing, and the one-time Voyager stack clones underF:\dev\voyager\wordpress\
Presets
Presets live in presets/ as JSON files. Load with --preset <name>.
Each preset defines:
mode: "ephemeral" or "continuous"wp_version: WordPress version (default: "latest")php_version: PHP version (default: "8.3" — wp-now caps at 8.3 as of 2026-05-07 and aborts on 8.4 with "Unsupported PHP version". SpinupWP supports 8.4 but accept the dev/prod skew. wp-env (continuous mode) does support 8.4. Bump back to 8.4 once wp-now ships support — track at https://github.com/WordPress/playground-tools)plugins: array of plugin sources (slug, URL, or local path)themes: array of theme sourcesconfig: wp-config.php constants to setenv_vars: environment variables (for license keys, etc.)
Available presets
| Preset | Mode | What's in it |
|---|---|---|
clean |
ephemeral | Vanilla WordPress, zero plugins |
voyager-stack |
ephemeral (project) | Fast wp-now Voyager preview. voyager-block-theme parent + voyager-core + voyager-blocks + voyager-orbit plugins. Mounts cwd as active child theme. Use Laragon persistent-client mode for net-new client builds that need MySQL and staging parity. |
voyager-base |
continuous | ACF Pro, Rank Math, WP Rocket, Voyager starter theme (legacy 3rd-party stack) |
gutenberg-dev |
continuous | Gutenberg trunk, test utils, Voyager Blocks |
Persistent client build workflow (Laragon)
Use this for canonical Voyager client child-theme work. Link to the SOP instead of copying every troubleshooting branch into the skill: [How-To] Infra: Local Development Environment (Laragon).
One-time machine setup:
$VOYAGER_DEV_ROOT = "F:\dev\voyager\wordpress"
git clone https://github.com/voyager-marketing/voyager-block-theme "$VOYAGER_DEV_ROOT\voyager-block-theme"
git clone https://github.com/voyager-marketing/voyager-core "$VOYAGER_DEV_ROOT\voyager-core"
git clone https://github.com/voyager-marketing/voyager-orbit "$VOYAGER_DEV_ROOT\voyager-orbit"
git clone https://github.com/voyager-marketing/voyager-blocks "F:\dev\voyager\voyager-blocks"
Per-client bootstrap:
git clone https://github.com/voyager-marketing/voyager-blank-child F:\dev\voyager-clients\<client-slug>
cd F:\dev\voyager-clients\<client-slug>
# Edit style.css header, Text Domain, theme.json, and patterns for the client.
git remote set-url origin git@github.com:voyager-marketing/<client-slug>-theme.git
powershell -ExecutionPolicy Bypass -File F:\dev\voyager-clients\<client-slug>\scripts\bootstrap-laragon.ps1
Verify:
- Visit
http://<client-slug>.test/ - Visit
http://<client-slug>.test/wp-adminasadmin/admin - Run
wp theme list --path=F:\laragon\www\<client-slug>and confirm the active child theme hasvoyager-block-themeas parent - Run
wp plugin list --path=F:\laragon\www\<client-slug>and confirm the required WP Plugins DB rows for this install profile are active
Voyager wp-now stack
The voyager-stack preset is the fast wp-now setup for Voyager stack previews.
It is useful when the user needs a short-lived child-theme preview or plugin test,
but Laragon is the canonical route for persistent client builds.
What it provides:
- Parent theme:
voyager-block-theme(slug:voyager) - Plugin suite:
voyager-core+voyager-blocks+voyager-orbit - The current working directory mounted as the active child theme
- PHP 8.3, WP latest, SQLite (no Docker)
- Persistent state via wp-now project mode
One-time setup (per machine) - pick one or both:
Option A - pull stable release artifacts (recommended for child-theme previews):
~/.claude/skills/wp-lab/scripts/voyager-dist-sync.sh sync
# Populates ~/.voyager-dist/<component>/<version>/ with `latest` symlink
Option B - clone working trees (only needed if you'll edit core/blocks/orbit source):
export VOYAGER_DEV_ROOT=/f/dev/voyager/wordpress
git clone https://github.com/voyager-marketing/voyager-block-theme "$VOYAGER_DEV_ROOT/voyager-block-theme"
git clone https://github.com/voyager-marketing/voyager-core "$VOYAGER_DEV_ROOT/voyager-core"
git clone https://github.com/voyager-marketing/voyager-orbit "$VOYAGER_DEV_ROOT/voyager-orbit"
git clone https://github.com/voyager-marketing/voyager-blocks "$VOYAGER_DEV_ROOT/voyager-blocks"
Per-client invocation:
cd f:/dev/voyager-clients/<client-slug> # e.g. va-exteriors
/wp-lab --preset voyager-stack # uses ~/.voyager-dist if present, else working trees
/wp-lab --preset voyager-stack --use-dev # force working trees (plugin-developer mode)
Dist folder vs working trees
The voyager-stack runner resolves each component via this precedence:
--use-devflag set → working tree at$VOYAGER_DEV_ROOT/<component>(skip dist)- Else
~/.voyager-dist/<component>/latestexists → use that - Else fall back to
$VOYAGER_DEV_ROOT/<component>working tree
When to use which:
- Dist (default) — child-theme dev. You want stable, released plugin code under your feet. Refresh via
voyager-dist-sync.sh syncwhen a new release ships. - Working tree (
--use-dev) — plugin-developer mode. You're actively editing voyager-core / voyager-blocks / voyager-orbit source. - Mix — dist runs by default, the dist sync script keeps
~/.voyager-dist/fresh; switch to--use-devonly when you need to live-edit a plugin.
The dist sync script (scripts/voyager-dist-sync.sh) downloads pre-built release zips from GitHub Releases via gh CLI (private-repo aware) with curl fallback for public repos. Versions are detected from each zip's Version: header; layout is ~/.voyager-dist/<component>/<version>/ with latest repointed to the most recent sync.
Per-client version pinning via voyager-stack.lock files is designed but not yet implemented — see Notion design doc for the spec.
What the engine actually does: when you invoke --preset voyager-stack,
scripts/wp-lab.sh builds a wrapper directory at
~/.wp-now/voyager-stack/<child-slug>/ and symlinks the parent theme + plugin
suite into its wp-content/, then runs wp-now from there in wp-content mode.
The wrapper persists between sessions; wp-now stores the DB at the hashed path
it derives from the wrapper. Symlinks are POSIX ln -s where supported and
fall back to Windows directory junctions via cmd /c mklink /J (no admin or
Developer Mode needed for junctions).
If cwd is not a theme directory (no style.css), the runner clones
voyager-marketing/voyager-blank-child into ./voyager-child-scratch and uses
that as the active child theme.
Native wp-now blueprint: blueprints/voyager-stack.blueprint.json
is a Playground/wp-now blueprint
template that achieves the same setup without going through the wp-lab skill.
Copy it into a client repo as blueprint.json and npx @wp-now/wp-now start
will pick it up automatically. Useful for sharing portable preview environments
or for collaborators who don't have the wp-lab skill installed.
Component reference: see the Voyager WordPress Stack — Component Reference page in Notion for the full component map (parent theme, plugin suite, blank child) with repo URLs, roles, and the canonical onboarding workflow.
Slash commands
| Command | What it does |
|---|---|
/wp-lab <target> |
Smart router. Detects mode from context or preset. |
/wp-ephemeral <target> |
Force ephemeral mode with wp-now |
/wp-dev <target or --preset> |
Force continuous mode with wp-env |
/wp-clean |
Tear down all running wp-env environments |
Target formats
- Plugin slug:
developer-tools(downloads from wordpress.org) - GitHub URL:
https://github.com/user/repo(clones the repo) - Local path:
./my-pluginor/absolute/path(mounts directly) - Preset:
--preset voyager-base(loads preset config)
Scripts
The automation engine lives in scripts/wp-lab.sh. The slash commands invoke
this script with appropriate flags. You can also run it directly:
./scripts/wp-lab.sh --mode ephemeral --target developer-tools
./scripts/wp-lab.sh --mode continuous --preset voyager-base
./scripts/wp-lab.sh --clean
Workflow
- User invokes a slash command or describes what they want
- Determine mode (
ephemeral,continuous, orpersistent-client) from command, preset, or context - Resolve target (download plugin, clone repo, validate local path, or route to the Laragon bootstrap)
- Generate config (.wp-env.json for continuous, directory mount for ephemeral, Laragon site folder for persistent-client)
- Launch environment
- Report the local URL and admin credentials
- On teardown, clean up temp directories and stop containers
Default credentials
- URL: http://localhost:8888 (wp-env) or http://localhost:8881 (wp-now)
- Admin: admin / password
- wp-now note: wp-now auto-logs you in as admin, no credentials needed
Theme dev workflow (block themes / FSE)
For canonical client child theme + pattern work, use the Laragon persistent-client workflow above. If the user chooses wp-now for a fast preview:
cd <theme-repo>→npx @wp-now/wp-now start --php=8.3(project mode, persistent)- Edit visually in WP Site Editor
npx wp-now wp create-block-theme save-changes→ writes Site Editor changes back totheme.jsonandparts/*.htmlin the repo (run from the same dir wp-now booted from)- Commit, push, let SpinupWP git-deploy pick it up on the dev server
wp-now WP-CLI cwd gotcha: npx wp-now wp <command> only works when run from
the same directory wp-now booted from. Running from a sibling dir produces
Error: Cannot find module 'package.json'. For the voyager-stack preset, that
means the wrapper dir at ~/.wp-now/voyager-stack/<slug>/, not the client repo.
This is the round-trip Automattic uses for first-party themes. It avoids
hand-tuning theme.json and keeps the repo as source of truth.
Migration to SpinupWP (production target)
The wp-lab skill spawns local environments only — it does not push to SpinupWP. For local → SpinupWP sync:
- Theme/plugin code: Use SpinupWP's native git-deploy. Push to a
devbranch, SpinupWP auto-pulls. No DB sync needed for code-only changes. - Database + media: Use WP Migrate (Delicious Brains, same vendor as SpinupWP) with saved push/pull profiles. Required when seeding pages / migrating content.
Troubleshooting
- Port conflict: If 8888 is taken, wp-env uses the next available port. Check output.
- Docker not running: wp-env fails silently sometimes. Run
docker infoto verify. - wp-now Windows quirks: Stick to PHP 8.3 (default). If you hit a native binary
crash, try
--php=8.3as a fallback. Older guidance recommended--php=8.1— do not do that; PHP 8.1 has been EOL since Nov 2024. - Plugin not found: The wordpress.org API slug must match exactly. Check https://wordpress.org/plugins//
- wp-now state seems lost: Check
~/.wp-now/wp-content/— the hash is derived from the absolute project path. If you moved the project, wp-now sees a new install. Move the data dir manually, or re-seed.
Related
- [How-To] Infra: Local Development Environment (Laragon)
- [How-To] Infra: SpinupWP New Site Provisioning
- Voyager WordPress Stack - Component Reference
- WP Plugins DB
Source: voyager-marketing/voyager-skills — distributed by TomeVault.