A command-line interface for LocalWP (Local by Flywheel). Lets you list, start, stop, restart, create, rename, inspect, shell into, and run WP-CLI against your Local sites without touching the GUI.
Replacement for the archived getflywheel/local-cli — plus site creation, which the original never supported.
LocalWP exposes an authenticated GraphQL API on 127.0.0.1:4000 while the app is running. lwp reads the auth token from your local config dir and talks to that API for control-plane operations (list/start/stop/create/rename). For per-site interaction (shell, wp) it spawns LocalWP's own bundled PHP and WP-CLI with the same environment LocalWP itself uses.
LocalWP must be running for any command to work.
- LocalWP 9 or newer, installed and run at least once
- macOS, Linux, or Windows (Git Bash/MSYS recommended on Windows)
- Nothing else —
lwpis a self-contained binary
curl -fsSL https://raw.githubusercontent.com/cartpauj/localwp-cli/main/scripts/install.sh | bashThis downloads the right binary for your OS/arch from the latest GitHub Release, drops it at ~/.local/bin/lwp, and makes it executable. If ~/.local/bin isn't on your PATH, the installer prints the line you need to add to your shell rc.
Grab the binary for your platform from the Releases page:
| Platform | Asset |
|---|---|
| macOS Apple Silicon (M1/M2/M3/M4) | lwp-darwin-arm64 |
| macOS Intel | lwp-darwin-x64 |
| Linux x86_64 | lwp-linux-x64 |
| Linux ARM64 | lwp-linux-arm64 |
| Windows x64 | lwp-windows-x64.exe |
Make it executable (chmod +x lwp-...) and move it to somewhere on your PATH (e.g. ~/bin/lwp).
If you have Node 20+ already, you can run directly:
git clone https://github.com/cartpauj/localwp-cli.git
cd localwp-cli
node src/index.js listRequires Bun:
curl -fsSL https://bun.sh/install | bash # if you don't have it
git clone https://github.com/cartpauj/localwp-cli.git
cd localwp-cli
bash scripts/build.shOutputs five self-contained binaries under dist/ — lwp-linux-x64, lwp-linux-arm64, lwp-darwin-x64, lwp-darwin-arm64, lwp-windows-x64.exe.
lwp doctor # verify Local is running and lwp can reach it
lwp list # see all your sites
lwp start my-site # start a halted site
lwp wp my-site -- core version
lwp shell my-site # drop into a shell with WP-CLI on PATHAnywhere a command takes <site>, you can pass:
- The site's ID (e.g.
luKnvcS4a) — exact match - Its name (case-insensitive) — exact match
- A unique substring of the name — e.g.
membermatchesmembers
If a substring matches more than one site, lwp errors with the list of matches and exits 1.
All commands accept --json to emit machine-readable JSON instead of human-readable text where applicable.
Lists all sites in a table: name, status, domain, ID. Sorted by name.
lwp list
lwp list --jsonLifecycle controls. Returns once Local has accepted the mutation; the site may take a few seconds to fully come up. Use lwp status <site> to confirm.
lwp start my-site
lwp stop my-site
lwp restart my-siteWith no argument, lists status for all sites (just name + status). With a site arg, prints one line: <name>: <status> (<url>).
lwp status # everything
lwp status my-site # oneFull site details: status, URL, domain, file path, web root, log dir, multisite mode, Xdebug state, services (PHP/MySQL/web server with versions), DB credentials, ID.
lwp info my-site
lwp info my-site --jsonOpens an interactive shell with the site's environment loaded:
cwdis the WordPress root (<site>/app/public)PATHincludes Local's bundled PHP and MySQL binaries plus the bundled WP-CLI- Library paths (
LD_LIBRARY_PATHon Linux,DYLD_LIBRARY_PATHon macOS) point at LocalWP's PHP shared libs - DB connection env (
MYSQL_HOME,MYSQL_UNIX_PORT,PHPRC) is set sowp db query,mysql, etc. just work
Exit the shell normally to return to your original session.
lwp shell my-site
# now inside the site's env
wp plugin list
mysql -e "SHOW DATABASES;"
exitRuns LocalWP's bundled WP-CLI against the site without entering an interactive shell. Everything after -- is forwarded to wp verbatim.
lwp wp my-site -- core version
lwp wp my-site -- plugin list --status=active
lwp wp my-site -- option get blogname
lwp wp my-site -- user create alice alice@example.test --role=editorOpens the site's URL in your default browser (open on macOS, xdg-open on Linux, start on Windows).
lwp open my-siteTails site logs from the filesystem. Default shows the last ~16 KB of every log; -f / --follow keeps streaming new output until you Ctrl-C.
source filters to one log type:
source |
What it shows |
|---|---|
all (default) |
nginx, php, mysql, mail |
nginx |
<site>/logs/nginx/* (error/access logs) |
php |
<site>/logs/php/* (error.log, php-fpm.log) |
mysql |
<site>/logs/mysql/* |
mail |
<site>/logs/mailpit/* |
lwp logs my-site # all sources, last 16 KB each
lwp logs my-site nginx # just nginx
lwp logs my-site php -f # follow PHP errors live
lwp logs my-site --source=mysql # equivalent flag formChanges the display name shown in LocalWP. Does not rename the site folder, the domain, or the URL — that matches LocalWP's GUI behavior.
lwp rename old-name new-nameCreates a new site. Without flags, runs interactively and prompts for each field. With flags, runs unattended — useful in scripts. Uses LocalWP's addSite mutation, then polls the resulting Job and streams its log output until success or failure.
Flags (any can be omitted to be prompted):
| Flag | Description | Default |
|---|---|---|
--name <n> |
Display name | required |
--path <p> |
Site directory | ~/Local Sites/<name> |
--domain <d> |
Hostname | <slug>.local |
--admin-user <u> |
WP admin username | admin |
--admin-pass <p> |
WP admin password | password |
--admin-email <e> |
WP admin email | admin@example.test |
--environment <e> |
preferred or custom |
preferred |
# interactive
lwp create
# scripted
lwp create --name test-site \
--domain test-site.local \
--admin-user admin --admin-pass secret \
--admin-email me@example.comSanity check — verifies LocalWP's config dir, that the auth token is readable, that the GraphQL endpoint responds, that a PHP 8.0+ install was found in lightning-services, and that LocalWP's bundled wp-cli.phar exists. Prints each check with ✓ or ✗.
lwp doctorPrints the CLI version.
Prints usage.
lwp does not implement delete or clone. LocalWP exposes these only through internal Electron IPC — they have no GraphQL mutations — so an external CLI can't trigger them safely while Local is running. Use the GUI for deletion and cloning.
Could not reach LocalWP at http://127.0.0.1:4000/graphql — LocalWP isn't running. Start the app.
GraphQL HTTP 401 — lwp auto-retries with a fresh token after Local restarts. If you still see this, run lwp doctor to confirm the token file is readable.
No PHP 8.0+ found in lightning-services — your LocalWP install is older than version 9, or its services haven't been updated. Open Local once, let it update, then retry.
wp-cli.phar not found — LocalWP's install dir isn't in any of the standard locations lwp checks. Open an issue with the path of your LocalWP install.
shell / wp errors about missing PHP extensions — usually means lwp picked the wrong PHP version. The site's specific PHP version is read from the GraphQL services field; if that's empty for some reason, the highest installed PHP is used. Run lwp info <site> to see what's reported.
lwp finds LocalWP on each platform via these paths:
| OS | Config dir | App install |
|---|---|---|
| Linux | ~/.config/Local, plus flatpak/snap variants |
/opt/Local, /usr/lib/local-by-flywheel, /snap/local/current, ~/.local/share/Local |
| macOS | ~/Library/Application Support/Local |
/Applications/Local.app |
| Windows | %APPDATA%\Local |
%ProgramFiles%\Local, %LOCALAPPDATA%\Programs\Local |
The auth token lives at <config>/graphql-connection-info.json. It rotates on every Local restart; lwp re-reads it automatically on a 401.
GPL v2 or any later version — see LICENSE.
- localwp-connect — Claude Code skill that connects a project directory to a LocalWP site (per-project
bin/wpwrapper, CLAUDE.md context block, plugin symlink). Complementary tolwp—lwpis the app-level control plane; the skill is per-project context.