Concepts
This page covers the rules every lps command shares. Each feature page then only documents what is specific to it.
Projects and environments
Section titled “Projects and environments”A project is one WordPress site you manage, and an environment is one installation of it: production, staging, local, or any name you choose. Each environment has its own URL and its own Application Password.
lps project config # Add or update a project/environmentlps project list # Show all configured projects and their environmentslps project switch # Interactively pick the active projectlps project remove # Remove a saved project or environmentlps project rotate # Rotate the Application Password of the active (or --env) environmentConfiguring an environment
Section titled “Configuring an environment”lps project config prompts for:
| Prompt | Description |
|---|---|
| Project name | A local identifier, lowercase, no spaces (e.g. my-site) |
| Environment | production, staging, development, or a custom name |
| WordPress URL | Full URL including scheme (https://example.com) |
| How to authenticate | Authorize in my browser (recommended) or Enter credentials manually |
By default the CLI runs a quick diagnostic against your site, then opens your browser to log in to WordPress and creates an Application Password for you automatically, no copy-pasting required. If the diagnostic fails or the browser flow can’t complete, it falls back to manual entry: your WordPress username and an Application Password you generate yourself under Users → Profile → Application Passwords. You can also choose manual entry upfront.
Browser authorization needs WordPress 7.0 or later: older versions refuse to send the Application Password back to the CLI on your machine. The diagnostic reads your site’s WordPress version and goes straight to manual entry on an older one. If the version is hidden (some security plugins remove it), the CLI tries the browser anyway; if WordPress then says the URL must be served over a secure connection, press Ctrl-C and run lps project config again with manual entry.
The browser flow talks to your site directly, with no Loopress server in between. The CLI opens a short-lived loopback server on 127.0.0.1, which WordPress redirects to once you approve, and only accepts the credential if the redirect carries the one-time state value the CLI generated. A request sent with an Origin header (a script on another site) is rejected. WordPress puts the Application Password in that redirect URL, so the confirmation page immediately rewrites its own address to remove it from your browser history.
If Loopress Full isn’t active on the site yet, project config then offers to install it for you, after a confirmation prompt: it downloads the latest release, creates a temporary administrator account, uploads and activates the plugin through a headless local browser (Chrome, Edge or Chromium must be installed), then deletes the temporary account. The account is removed even if the install fails or you press Ctrl-C. If the process is killed outright or the network drops before that, Loopress Full removes the leftover account within the hour once it’s active, and shows a notice in wp-admin until then. If the install stopped before Full was activated, the next lps project config run on the same site removes it, and lps doctor reports it until then. This step is independent of the authentication mode. If the automatic install can’t complete (some managed hosts block file uploads via DISALLOW_FILE_MODS), the command falls back to instructions for a manual upload.
Where configuration lives
Section titled “Where configuration lives”| File | Scope | Holds |
|---|---|---|
$XDG_CONFIG_HOME/loopress/config.json (or ~/.config/loopress/config.json) |
Your machine | Projects, environments, URLs and Application Passwords, plus the active project |
loopress.json |
Your repository | Which project the repository belongs to, where each kind of file lives, pinned plugins and themes |
Credentials never go in the repository. To share projects between machines, see Loopress Account.
Targeting an environment
Section titled “Targeting an environment”Commands run against the project set by projectId in loopress.json, or the active project (lps project switch) when there is none. Within that project, the environment is never remembered between commands:
--env <name>when passed.- Otherwise the
localenvironment, if the project has one. - Otherwise the project’s only environment.
A project with several environments and no local one needs --env on every command. So reaching a remote site is always spelled out in the command itself:
lps snippet push # locallps snippet push --env staging # staginglps status --env staging # preview what would be targeted--env errors with the list of available environments if the name does not exist.
Common flags
Section titled “Common flags”These flags work the same way on every command that accepts them. Feature pages only list the flags specific to a command.
| Flag | Description |
|---|---|
--env <name> |
Target this environment instead of the active one. Accepted by every command that talks to WordPress. |
--dry-run / -d |
Show what would happen without changing anything, see dry runs. |
--yes / -y |
Answer yes to confirmations: deleting local files on pull, pushing to production, --prune, removals, rollbacks. |
--overwrite |
Push even over changes made on the site outside Loopress since your last push, see changes made outside Loopress. Not implied by --yes. |
--json |
Print a structured result instead of formatted text, for scripts and the MCP server. |
How pull and push treat your files
Section titled “How pull and push treat your files”Pull mirrors the site
Section titled “Pull mirrors the site”A pull writes one local file per object on WordPress, and removes local files whose object no longer exists there, so the directory always mirrors the site. In a terminal, the files about to be removed are listed and a confirmation is asked first (--yes skips it). In scripts and CI they are removed and reported, so existing pipelines keep working. Each feature page states which local files count as tracked, files outside that convention are never touched.
Single-file resources (menus/menu-locations.json, Global Styles) are always overwritten in place.
Every file carries its identity
Section titled “Every file carries its identity”Each resource identifies an object by a stable value stored in the file: a snippet’s or form’s id, an ACF object’s key, a menu’s or a post’s slug, an option’s name. push uses it to update the right object even if you rename the file. That’s why you should pull before editing locally: a file written by pull always carries its identity.
When a file’s id doesn’t exist on the target site (a fresh install, another environment), push creates the object instead and renames the local file to match the id WordPress assigned.
Push never deletes by default
Section titled “Push never deletes by default”A push creates and updates, it doesn’t remove what you don’t have locally. The exceptions are explicit: --prune on lps api push and lps hook push deletes server files absent locally, --prune on lps plugin push deactivates untracked plugins, removing an entry from the plugin lockfile uninstalls it, and the remove commands delete one object on purpose.
Every real push also saves a snapshot of what it replaced, so you can undo it with rollback.
Changes made outside Loopress
Section titled “Changes made outside Loopress”Before writing, a push compares the site with the state your last push from this machine left it in. If an object was changed there since (an edit in wp-admin, another tool) and the push would write a different version over it, the push stops and names those objects:
Changed on https://acme.com outside Loopress since your last push (2026-10-12T09:14:02.000Z), this push would overwrite: header-menu.Review the difference with lps diff --only <resource>, then either bring the site’s version into your files (pull, or copy the change by hand), or push with --overwrite to replace it. In a terminal the push asks instead of stopping, and a --dry-run only warns.
Objects added on the site and absent from your files are never flagged, a push doesn’t touch them. Nor are objects deleted on the site (in wp-admin, or with a remove command): pushing them again overwrites nothing. Once a pull brings the change into your files, the push goes through.
This relies on the snapshots kept on the machine that pushed, so it protects a push from the same machine. The first push from a new clone or a CI runner has nothing to compare with and isn’t checked. Templates and template parts don’t need it: a Site Editor edit stays in the database and keeps winning over the pushed file, and the push warns about it.
Dry runs
Section titled “Dry runs”Most commands accept --dry-run (-d), which shows what would happen without making any changes:
lps snippet push --dry-runlps composer push --dry-runA push’s dry run still reads the target site, the same way lps diff does, so it fails when the real push would be refused: for example lps acf push --dry-run on a site where ACF isn’t active.
It also says, for each local item, what the push would do to the site’s copy: created (the site doesn’t have it), changed or unchanged. A file whose local and site copies match shows as unchanged, so you can see at a glance what the push will really change:
$ lps hook push --dry-run[dry-run] unchanged: bakery-info[dry-run] changed: productsWith --json, the same list is the result’s changes array ({"id": "products", "status": "changed"}), next to the fields the result already had. A few pushes say more:
lps acf push: ids are<type>/<key>(field-groups/group_product_details), and a changed field group names its top-level fields underfields(added,changed,removed).lps theme template push:changesis split intotemplates,partsandcss.csshasstyle.css, the bundle oftheme/css/, and one entry pertheme/blocks/file. The child theme mirrors the project, so a file it has and the project doesn’t isremoved. A CSS file isunknownwhen the site’s Loopress plugin is too old to report its CSS.lps pushandlps theme push: each step underresultscarries that step’s own result inresult.
lps plugin push and lps theme version push don’t need them: their dry run already lists what it would install, update or remove. lps composer push and lps app push don’t list per-item statuses yet.
The statuses use the same comparison as lps diff, so a value WordPress stores in another form than your file (a default ACF fills in, a Global Styles value WordPress rewrites) makes the item changed even when the push wouldn’t change what the site does.
CI and non-interactive use
Section titled “CI and non-interactive use”Without a TTY, or when the CI environment variable is set, the CLI never hangs waiting for a prompt:
- Confirmations take their default answer and log it. Pass
--yes(-y) to answer yes explicitly. - Commands that require interactive input (
lps init,lps project config) fail immediately with instructions. Configure projects on your machine and commitloopress.json; in CI, target environments with--env. - Pushing to an environment named
productionasks for confirmation in a terminal, and requires--yesin CI:
lps snippet push --env production --yes- A push that would overwrite changes made on the site outside Loopress fails and names them. Pass
--overwriteto push anyway, see changes made outside Loopress. - Pull commands that would delete local files no longer present on WordPress remove them and report them, see pull mirrors the site.
See CI/CD Integration for complete pipelines.
Error reporting
Section titled “Error reporting”Loopress sends crash reports to Sentry so bugs can be found and fixed. A crash report includes the command name, its flags and arguments as typed, your Node.js version, and OS. WordPress credentials are never included.
lps telemetry disable # Persists across all future commandslps telemetry enable # Re-enableOr set LOOPRESS_TELEMETRY_DISABLED=1 to override the persisted preference for a single run, useful in CI.
Troubleshooting
Section titled “Troubleshooting”When a command fails and the cause is unclear, run lps doctor: it checks that the site is reachable, the plugin installed, and the credentials valid, each with a corrective action. If authentication fails with a 401, see Application Passwords.