Skip to content
febritecnoPublic

About

modern bridge between local AI agents and live WordPress sites. It turns WordPress into an AI-native environment by providing secure auth, file synchronization, atomic snapshots, rollback capabilities, and a self-healing lint gate

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

WPicker Logo

WPicker: AI-Native WordPress Bridge

License: GPL v2+ VirusTotal Go Version PHP Version

WPicker (a playful nod to the hardworking woodpecker 🐦) lets you develop a WordPress site on your own machine — with or without an AI agent — and move the work to the live site with one command, instead of the usual backup → upload → restore routine.

It has two halves:

Runs on Does
Plugin the WordPress site Receives changes, checks them, keeps snapshots and backups
CLI (wpicker) your computer Pulls, pushes, merges, clones

Warning

Use it on staging, development, or local sites, or on production only with backups you have tested. Deactivate and remove the plugin when you are done with it.

Preview

Every push is a snapshot you can inspect, revert or restore
Snapshots: search, filters, status and per-push actions
Full-site backup and restore, or restore from a file
Backup & Restore: create, upload, restore, download
Scan installed plugins for known vulnerabilities
Security scan: run a scan and download the report

Contents: Quick start · Daily work · Working as a team · Undoing things · Backup & restore · AI agents · Command reference · Limits


Quick start

1. Install the plugin on the site

  1. Download wpicker.zip from the latest release.
  2. In WP-Admin go to Plugins → Add New Plugin → Upload Plugin, choose the zip, Install Now, then Activate.
  3. A WPicker menu appears in the sidebar.

The site needs:

  • PHP 7.4 or newer, with the php command available to the web server (used to syntax-check pushed files).
  • An active child theme, if you want to sync theme files. WPicker only ever writes inside the child theme — never the parent theme, core, or wp-config.php.
  • HTTPS (or WP_ENVIRONMENT_TYPE set to local): WordPress only hands out Application Passwords on secure sites, and WPicker pairs through one.
  • The edit_themes capability for whoever approves a device (on multisite: a super administrator). A site that sets DISALLOW_FILE_EDIT removes it from everybody.

2. Install the CLI on your computer

macOS / Linux:

curl -sL https://raw.githubusercontent.com/febritecno/wpicker/main/install.sh | bash -s -- install
wpicker --help

The installer also connects the AI agents it finds on your computer (Claude Code, OpenCode, Codex, Gemini CLI, Goose), so /wpicker works in Claude Code right away. Skip that with bash -s -- install --no-agents.

wpicker connect does the same at any time. Global is the default, and you can pick which agents:

wpicker connect                      # every agent found on this computer, for all your projects
wpicker connect claude opencode      # just those (same as --agent claude,opencode)
wpicker connect --agent all          # every adapter
wpicker connect cursor               # a tool with only per-project rules: written into this project
wpicker connect claude --project     # this project only
wpicker connect list                 # what is connected, current or stale
wpicker connect remove claude        # take WPicker out again

Built in: claude, opencode, codex (AGENTS.md), gemini, goose, cursor, copilot, windsurf, cline, roo, zed, kiro. The locations and formats of these tools' rule files are from memory of their conventions, not checked against each tool's current documentation; if one stops working, tell us (each is one line in cli/cmd/connect.go).

Your own adapters. Anything else that reads an instruction file can be connected without changing WPicker: drop a JSON file in ~/.wpicker/adapters/ (never read from a project, so a cloned repository cannot add one):

{"key": "mytool", "name": "My Tool",
 "global": ".mytool/instructions.md", "project": "MYTOOL.md",
 "mode": "block", "content": "stub", "detect": ["~/.mytool"]}

mode is block (a marked section in a file you also write in) or file (a file of WPicker's own, with optional frontmatter: skill, cursor or windsurf); content is stub (a short pointer) or guide (the full guide). Paths must be relative and stay inside their folder; a bad file is ignored with a reason in wpicker connect list and wpicker doctor.

Safety: the instruction files are only written inside the project (never through a symbolic link that leads out of it), a file you wrote is never replaced without --force, and login asks before touching a project. Run connect as yourself, not with sudo. Your pairing is per user, not per project, so when you open a repository you do not trust, do not let your agent run wpicker freely there: allow only the read-only commands (map, logs, find, files, db, guide) and keep theme push and rollback on ask.

Agents that read a file in every conversation get a short pointer and fetch the full instructions with wpicker guide only when the task is about the site. Files you already have (AGENTS.md, GEMINI.md) only get a marked block added. Run wpicker connect again after upgrading to refresh.

Change install to remove to uninstall. To build from source instead (Go 1.21+):

git clone https://github.com/febritecno/wpicker && cd wpicker
make build-cli          # → bin/wpicker

Keep the plugin and the CLI on the same version; wpicker login warns when they differ.

3. Connect the CLI to the site

wpicker login https://your-site.com

It prints a link and opens it in your browser. There you log in to WordPress as you always do (so your password and any two-factor plugin apply), see which site and which device are asking, check that the code matches the one in your terminal, and press Approve. The terminal then finishes by itself. Nothing is typed into it, and your password never reaches the CLI.

  • Only approve a request you started yourself, a moment ago, on your own computer. Never approve a link somebody sent you. When the request came from a different network than your browser (for example, you ran the command on a server over SSH), WP-Admin makes you type the code from that terminal before it lets you approve.
  • Add --no-browser to only print the link. The plugin must be 1.3.0 or newer; the older pairing with an admin password and a PIN has been removed, so update the plugin and the CLI together.
  • Behind a proxy or CDN (Cloudflare, a load balancer, some managed hosts): WordPress then sees every visitor as one address, so WPicker cannot tell whether the request came from your network, and always asks you to type the code. wpicker doctor says when this applies. If your proxy sets the real address in a header and strips it from outside requests, tell WPicker: add_filter( 'wpicker_client_ip', fn() => $_SERVER['HTTP_X_FORWARDED_FOR'] ?? $_SERVER['REMOTE_ADDR'] );
  • Typing the code is a speed bump against someone who sends you a link, not a wall: a determined phisher can simply tell you the code. Do not approve requests you did not start.
  • Approving needs the edit_themes capability (on multisite: a super administrator). On a site that sets DISALLOW_FILE_EDIT, WordPress removes that capability from everyone, and no pairing method of WPicker works until it is lifted.
  • The CLI keeps a separate, revocable credential for this device in ~/.wpicker/sites.json (readable only by you); your own password is never stored. WordPress emails you when a device is paired, and WP-Admin → WPicker → Devices shows who just joined, with a Revoke it button.
  • You can pair several sites; logging in to another one does not touch the first. See them with wpicker sites.
  • Check which site a folder uses with wpicker whoami, or ask the site to describe itself with wpicker context.
  • Remove a site any time with wpicker sites delete <id> (it also revokes this computer on the site), or from WP-Admin → WPicker → Devices.

Several sites and several folders

Each project folder works with one site. wpicker theme pull links the folder to the site it pulled from (a small .wpicker/project.json, no secrets), and from then on every command in that folder uses that site. For any other folder:

wpicker sites                  # id, name, address, user, status, linked folders
wpicker sites link shop        # use "shop" in this folder from now on
wpicker --site blog logs       # one command against another site
wpicker sites stop blog        # keep its login but refuse to use it (start to undo)
wpicker sites delete blog      # forget it and revoke this computer on it

With one site nothing needs choosing. With several, commands that only look (logs, map, files) fall back to your default site (wpicker sites use <id>) and say so on stderr; commands that change a site (theme push, rollback, page, restore) refuse to guess and ask you to link the folder or pass --site. --site takes an id, name or address of a site you are already logged in to; it can no longer point your login at another address.

Upgrading: your existing login is picked up automatically as site 1. ~/.wpicker/config.json is left untouched, so an older wpicker binary on your PATH keeps working with it.

4. Get the site onto your computer

Pick one.

A. The whole site — database, plugins, themes, uploads. This is the one that replaces "export a backup, import it locally".

Create an empty WordPress locally first (for example with Local), then:

wpicker clone --path "/path/to/local/wordpress"

--path is the folder containing wp-config.php. The command backs the site up, downloads one archive, unpacks it, loads the database and rewrites every address to your local one. Your previous local database and folders are saved to wpicker-before-restore-<time>/ first. Afterwards you log in locally with the same accounts as on the site.

Database settings are read from the local wp-config.php (sites made with Local are detected automatically). Override them if needed: --db-host, --db-port, --db-socket, --db-name, --db-user, --db-pass, and --url for the local address.

B. Only the child theme — when you just want to work on theme code.

mkdir my-child-theme && cd my-child-theme
wpicker theme pull

After a clone you already have the theme: cd into wp-content/themes/<child-theme> and carry on from there.


Daily work

Run these inside the child theme folder.

wpicker theme status     # what would be pushed, what could be pulled, conflicts
# ...edit files, check them on your local site...
wpicker theme push       # send your changes to the site

What push does on the site, in order:

  1. Syntax check every PHP file. An error rejects the push; nothing is written.
  2. Snapshot the theme as it is now.
  3. Write your files (and delete the ones you deleted).
  4. Health check: load the site. If it now returns a server error, the snapshot is put back automatically and you get the PHP error with file and line.

So a bad push does not take the site down, and you are told what to fix.

To pick up changes made on the site:

wpicker theme pull       # lists them; you choose which to apply
Changes on https://example.com not in your local copy:
   1) A  inc/helpers.php
   2) M  style.css
   3) C  functions.php   (changed on both sides — merged line by line; …)
Apply which? [a]ll (merging where needed) · numbers (1,3 or 2-4) · d <n> to view diff · [q]uit >

wpicker theme diff [file] shows the line-by-line difference between your copy and the site.


Working as a team

Several people (or agents) can be paired with the same site. WPicker never lets one silently overwrite another.

If someone pushed while you were editing, your push is refused and status says who:

Conflicts (changed on both sides):
  C  inc/shop.php   (ani-laptop, 2026-10-13 14:02)

Run wpicker theme pull. Files changed on both sides are merged line by line, like git:

  • Edits to different lines are simply combined.

  • Where you both changed the same lines, both versions are kept between markers:

    <<<<<<< yours
    	return 'IDR ';
    =======
    	return 'Rp';
    >>>>>>> site (ani-laptop, 2026-10-13 14:02)
    

    Open the file — VS Code, Cursor and JetBrains show their merge controls for these — settle each block, delete the marker lines, and push. A push is refused while markers remain. Your pre-merge version is in .wpicker/merge-backup/.

To take one side outright instead of merging:

wpicker theme pull --theirs functions.php       # the site's version
wpicker theme pull --keep-local functions.php   # yours; the next push overwrites the site's

Images, fonts and other binary files cannot be merged; you choose a side.

Review before it goes live. In WP-Admin → WPicker → Settings, set When a device pushes to Review first. Pushes are then held: an administrator opens Snapshots, reads the diff, and clicks Apply to site or Reject. A device cannot approve its own push.


Undoing things

Every push is recorded under WP-Admin → WPicker → Snapshots (search, filter, and a side-by-side diff of each file). From there, or from the CLI:

Action What it does
Revert this push Undoes that one push. Later pushes are kept.
Restore to before this (wpicker rollback <id>) Puts the whole theme back to how it was before that entry. Later pushes are undone too.

wpicker snapshots lists them. If a revert collides with a later change, you decide per file which version stays.


Backup & restore

WP-Admin → WPicker → Backup & Restore — full-site backups, each a single .tar file.

  • Create a backup of the database, plugins, themes and uploads.
  • Restore any listed backup onto the site. The database is loaded into temporary tables first and switched over at the end, and the current site is backed up before anything changes.
  • Restore from a file — upload a backup made on another site. Addresses are rewritten to the destination's automatically, so this is also how you move a whole site from one place to another.

From the CLI:

wpicker backup list
wpicker backup create --note "before redesign"
wpicker backup restore --path "/path/to/local/wordpress"   # pick one, rebuild it locally
wpicker clone --path "/path/to/local/wordpress"            # create + restore in one go

Restoring replaces the destination's content and user accounts with the backup's; you log in again with an account from the backup.


Diagnosing a site

Off by default. Turn on Diagnostic access in WP-Admin → WPicker → Settings; a paired administrator device (you, or your AI agent) can then look at the site without changing it:

wpicker logs --since 1h                 # PHP + WordPress errors, grouped, with file and line
wpicker debug on                        # record every warning, notice and deprecation…
wpicker logs --since 5m --trace         # …reproduce the problem, then read them
wpicker debug off

wpicker map                             # what is installed: plugins, themes, WooCommerce gateways, email plugins
wpicker find woocommerce_thankyou --kind hook   # where a function, class, hook or REST route is defined
wpicker logs --source wc:stripe         # WooCommerce and payment-gateway logs (new level: error)

wpicker files ls plugins/my-plugin      # any code under wp-content
wpicker files cat plugins/my-plugin/main.php --lines 80-120
wpicker files grep "add_action( 'init'" plugins

wpicker db tables
wpicker db query "SELECT ID, post_title, post_status FROM wp_posts ORDER BY ID DESC LIMIT 10"
  • Logs come from PHP's error log, wp-content/debug.log, and WPicker's own log. wpicker debug on does what WP_DEBUG + WP_DEBUG_LOG do — everything is logged, nothing is shown to visitors — without editing wp-config.php.
  • Secrets are masked by their shape, not only by the name next to them: payment keys, bearer tokens, JWTs, card numbers (checked with the Luhn test, so order numbers stay readable) and CVCs, including inside print_r dumps and in lines cut by the length limit. The names of log files are never sent.
  • Map and find read an index of the site's PHP (functions, classes, methods, hooks, shortcodes, REST routes, post types, options), kept per file so a second search takes milliseconds. wpicker map marks plugins that are the site's own, and a plugin that is a symbolic link out of wp-content (it cannot be read there, and says so).
  • Files are read-only and limited to wp-content. wp-config.php, backups, key files and database dumps are not available.
  • Database queries are read-only: SELECT, SHOW, DESCRIBE, EXPLAIN, one statement, run in a read-only transaction. Credentials are out of reach — the password columns do not exist for these queries, rows holding keys, tokens, salts or sessions are left out, and tables that are credential stores by name are refused.

Everything else in the database can be read, including customer and personal data, and whatever an AI agent reads is sent to its provider. Each access is listed under Settings → Recent diagnostic access. Switch it off when you are done.


Using it with an AI agent

There is nothing to set up per project for the agents that have a global setting. The installer (or wpicker connect) teaches Claude Code, OpenCode, Codex, Gemini CLI and Goose about WPicker, so /wpicker works in Claude Code right away. For a tool that only has per-project rules (Cursor, Copilot, Windsurf, Cline, Roo, Zed, Kiro), run wpicker connect <name> in the project; wpicker login offers to do it when it finds one there. wpicker update refreshes the instruction files WPicker wrote, so they never go stale, and wpicker guide prints the full instructions an agent follows.

  • Every command takes --json for machine-readable output.
  • wpicker context gives the agent the facts it would otherwise guess: WordPress and PHP versions, active theme, plugins, theme settings.
  • A failed push writes .wpicker/last-error.json with the error code, message, file and line, so the agent can fix and retry on its own.
  • Without a terminal, wpicker theme pull applies everything and merges where needed instead of prompting; wpicker backup restore needs --yes.

Works with any agent that can run shell commands (Claude Code, Cursor, Copilot, and others).


Command reference

Global flags: --json, --site <id|name|address>, -v.

Command Purpose
wpicker login [address] Pair this computer with a site: open the link it prints and approve it in your browser (--no-browser, --device, --name)
wpicker sites List your sites with live status and linked folders
wpicker sites login|stop|start|delete|use <id> Re-pair, switch off/on, forget (revokes this computer), make default
wpicker sites link [id] / unlink Use a site in this folder from now on / stop
wpicker whoami Which site this folder uses, and why
wpicker doctor Checklist: is WPicker itself set up and working
wpicker device list / revoke <id> Advanced: every computer paired with the site
wpicker context [--save] Site facts for you or your agent
wpicker connect [agent…] Connect AI agents and other adapters (global by default; --project, --agent all, --refresh); connect list / connect remove <agent>
wpicker guide Print the full agent instructions
wpicker theme status To push, to pull, conflicts — and who pushed
wpicker theme diff [path…] Line diff, local vs. site
wpicker theme pull [path…] Apply changes from the site, merging where needed
--all --dry-run Everything without asking / list only
--theirs <path> --keep-local <path> --force Take the site's version / keep yours / make local match the site
wpicker theme push Send local changes (--dry-run, --all)
wpicker theme files List the theme's files on the site
wpicker snapshots (alias history) Push history
wpicker rollback <id> Restore the theme to before a snapshot
wpicker backup list / create / download <id> / delete <id> Full-site backups on the site
wpicker backup restore [id] --path <dir> Rebuild a backup on a local WordPress
wpicker clone --path <dir> Back up the site and restore it locally
wpicker page create --title … --builder divi|elementor|gutenberg --file … Create a page from a builder payload
wpicker page update <post-id> --builder … --file … Update one
wpicker map What is installed: plugins, themes, WooCommerce gateways, email plugins
wpicker find <name> [--kind …] [--in …] Where a function, class, hook, shortcode, REST route, post type or option is defined
wpicker logs (--since, --level, --grep, --trace, --source wc[:name]) The site's errors, grouped (including WooCommerce and payment gateways)
wpicker debug on / off / status WordPress debug logging
wpicker files ls / cat / grep Read code under wp-content
wpicker db tables / db query "<sql>" Read-only database access, without credentials
wpicker scan Check installed plugins against a vulnerability database
wpicker update Update the CLI

wpicker <command> --help has the details.


What it does not do

  • Theme sync is the child theme only. Plugins, uploads and the database travel through backups, as a whole — not file by file or page by page.
  • Restoring a backup from the CLI targets your computer. Restoring onto a site is done in its WP-Admin.
  • Pairing needs a browser that can open the site's WP-Admin. It can be on another computer than the one running the command (type the code it shows); a machine with no browser at all cannot pair.
  • Multisite networks are not supported for backups.
  • Email addresses at the old domain are left as they are when a site is moved.
  • Sites of many gigabytes have not been tested.

Developing WPicker

make dev        # WordPress in Docker (wp-env) at http://localhost:8888, admin / password
make build-cli  # bin/wpicker
make test       # Go tests + PHP syntax check
make sim        # end-to-end push / pull / merge scenarios against two local sites
make mirror     # site-to-site backup + restore check — overwrites the target site

context.md describes the architecture, the REST API and how each piece works.

🛡️ Security & VirusTotal

The CLI binaries and the plugin zip are built from this repository. You are encouraged to scan downloaded release assets with VirusTotal; new Go binaries on Windows are sometimes flagged by mistake.

Pairing uses WordPress Application Passwords, one per device and revocable; your administrator password is never stored. Approving a held push, deleting snapshots and restoring a backup onto a site can only be done by an administrator signed in to WP-Admin — not by a paired device.


📜 License

All Rights Reserved.

About

modern bridge between local AI agents and live WordPress sites. It turns WordPress into an AI-native environment by providing secure auth, file synchronization, atomic snapshots, rollback capabilities, and a self-healing lint gate

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages