You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
WPPilot 1.13.0 — Runs beside Elementor's MCP, with security fixes
WPPilot no longer fatals beside another plugin that bundles the same dependencies. Composer derives the Jetpack autoloader's namespace from the dependency set, so two plugins with similar composer.json files generate the identical namespace and the second one to activate dies with "Cannot declare class Automattic\Jetpack\Autoloader\jp\al\Autoloader, because the name is already in use" - a fatal that names a hashed class nobody can search for and reads as a WPPilot bug. The namespace is now pinned to one WPPilot owns, so it cannot collide with a hash another project happened to generate. On WP_DEBUG sites the autoloader's own conflict check is switched on as well, so the next collision of any kind is logged by name instead of appearing as a white screen.
WPPilot now shares a site with Elementor's MCP, and with any other plugin that ships the WordPress MCP Adapter. Only one copy of the adapter can ever load - its classes are global and unprefixed - and which copy wins is decided by version arbitration across every plugin's classmap, so WPPilot can find itself running Elementor's copy. class_exists() cannot see this: it answers true whoever provided the class. WPPilot now resolves the adapter's actual origin and behaves accordingly. When the copy is its own, nothing changes. When it is not, WPPilot stops renaming the adapter's default server and stops claiming the legacy alias slug - that server belongs to the plugin that bundled the adapter - and registers /wp-json/mcp/wppilot explicitly instead, so every existing client configuration keeps working rather than 404ing the moment another MCP plugin is installed. Creating a server whose id is already taken is skipped rather than thrown, because that throw happens inside mcp_adapter_init and would take down every server on the site rather than one registration.
wppilot/system-status reports which copy of the adapter is loaded, its version, and the plugin directory that owns it. On a site where MCP behaves oddly and more than one MCP plugin is installed, that is the first line to read.
New ability: wppilot/elementor-audit-atomic-readiness. Elementor 4 introduced atomic elements, and the tooling built on them - Elementor's own MCP included - works on atomic documents. Every site built before that is classic widgets, and the failure is not an error message: the tool connects, reports success, and cannot edit the page anyone asked about. Nothing on the site says why. This reads every Elementor document rather than one, classifies every element, and answers it in one call: how much is already atomic, how much WPPilot can convert, which widget types block the rest and in how many documents. A widget whose plugin has been deactivated is reported as unregistered rather than counted as classic, because those elements are lost rather than converted and that has to be known before anybody starts.
The classic-to-atomic equivalents the audit counts on live in one table the converter reads too, so an audit that calls a widget mappable and a converter that then cannot map it is not a state this can reach. A widget with no honest structural equivalent is absent from it: icon-list can be rebuilt as a flexbox of paragraphs, but that is a reconstruction with its own losses and belongs in a conversion report rather than in a table claiming equivalence.
wppilot/verify-rendered-page can now check a page that is not published. It used to refuse - a draft has no public URL, and fetching one anonymously returns the 404 rather than the page - which sent the check away at exactly the moment it was most wanted, while the page was still a draft. The preview is now fetched carrying the caller's own session, opened for the request and destroyed as soon as it returns. Nothing is escalated: the cookies authenticate the current user and nobody else, they are valid for a minute, they are only ever attached to a URL on this site, and a caller who cannot read the draft in wp-admin cannot read it here. The result says it was a preview, and says what a preview does not exercise.
New ability: wppilot/get-page-view-link. Every other check in WPPilot reads the page; none of them can see it. Whether a headline is legible over the photograph behind it, whether the third card wrapped onto its own row at 1024px - those are visual facts, and reading HTML answers none of them. Most AI clients already have a browser, so this closes the loop with the one they have rather than by building a renderer: it returns the URL to open and the three widths worth looking at. For a draft it says a signed-in browser is needed and names wppilot/create-admin-access-link, rather than minting a session itself: a read-only ability that handed out a wp-admin session would have walked past the critical rating, the confirmation and the safety profile that guard that link.
Visual verification. Every other check in WPPilot reads the page; none of them could see it, and the failures that matter most to whoever owns the site are visual - a headline illegible over the photograph behind it, a card that wrapped onto its own row at 1024px, an element that rendered and is invisible. The rendered check has listed visual-regression and responsive-breakpoints under not_checked since it was written. This is that gap: wppilot/register-capture records a screenshot against the page it shows, wppilot/list-captures finds the one to compare against, and wppilot/compare-captures reports how much changed and exactly where.
The comparison samples a grid of average colours rather than comparing pixels. A pixel-exact diff reports every page as changed - a font rasterises differently, an image is re-encoded, a carousel is on another slide - and a check that cries wolf is a check that gets switched off within a day. Adjacent changed cells are merged into regions, with one cell of slack so the gap between two words in a changed headline does not split one finding into two, and the regions come back in the coordinates of the newer image so they can be pointed at. A page that got taller is reported separately, because sampling normalises both images to the same grid and added content would otherwise shift every cell rather than show up as a region.
WPPilot still bundles no renderer and never will: a headless browser inside a WordPress plugin is not something a shared host survives. The screenshot is taken by a browser that already exists - the AI client's own, through wppilot/get-page-view-link - and these abilities store what comes back and compare it. Captures are attachments and everything refers to one by id, because a preview record is capped at 256KB and the whole change ledger at 4MB: a base64 screenshot in either would evict real history to store a picture.
Ten captures are kept per page, oldest pruned on write. Comparing two captures taken at different widths is answered with the comparison and a warning, because "everything changed" is true there and useless.
A queued block-editor change whose target page was deleted stranded its whole batch. The queue already refused a missing target at commit, but an item never reached commit: it was handed to a browser tab that cannot open a post which is not there, and the tab had nothing useful to report back, so the item sat running until its lease expired and the batch never finished. The target is checked when the item is claimed now, so it fails once, names the reason, and the rest of the batch carries on.
The Prompts screen is usable at three hundred briefs rather than ten. Every brief printed open, which came to 2.7MB of markup before anybody saw the first one; bodies are collapsed now - still in the page, so Copy and find-in-page reach them - and a filter sits above the sector headings.
A brief's scope survives the parser. It says whether a brief builds a landing page, a whole site, a store, a course or a booking site; free's own briefs are all landing pages and leave it empty, and a field the normaliser dropped was one nothing could show or filter on.
Five new abilities, one of them a write that only records an attachment as a capture. One existing ability widened. No permission changes; existing connections keep working and do not need re-authorising.
WPPilot runs on PHP 8.0 again, as its header promises. Four call sites used first-class callable syntax (intval(...), is_array(...)), a parse error before PHP 8.1: the design capture and visual-runtime files, and the new Elementor atomic-readiness audit. They pass the function name instead.
Security: the modern MCP transport answered tools/list and server/discover before WordPress had checked who was asking, so an anonymous request could read every ability's schema and the safety profile, and the transport ignored the AI Abilities master switch. It now answers only a caller who passes the same checks as the legacy route, and everyone else gets the normal 401 challenge. Also in 1.12.0.
Security: WPPilot access tokens and OAuth bearer credentials were honoured on every request, not only the REST API, so a Read Only token could post a comment or drive admin-ajax as the administrator. They are accepted on REST requests only, as WordPress scopes Application Passwords.
Security: wppilot/verify-rendered-page sent the caller's session cookies to wherever a draft's preview redirected. A signed-in check no longer follows redirects, sends cookies only to this site over the same scheme and port, and every other fetch it makes validates each redirect hop.
Captures: pruning a page's capture history permanently deleted any media item that had been registered as a capture - a logo or product photo could go. Only files WPPilot created are deleted now; anything else is simply unmarked. Captures are looked up per page, so a quiet page's history no longer disappears behind busier pages, and the capture runtime requires upload rights and edit access to the page.
Rollback: restoring a post's meta stripped a level of backslashes, so Elementor, Bricks, Breakdance and Oxygen data came back as JSON that no longer decoded. Terms, comments and menus were restored and then reported as failed, because they never reported verification; they compare a fresh snapshot now.
wppilot/elementor-audit-atomic-readiness is listed over MCP and honours the master switch; it registered but was never served. Modern-transport writes count against the write rate limit.
PHP 8.0: seven more places unpacked string-keyed arrays, which PHP 8.0 refuses at run time - among them every successful elementor-set-content call and every preview apply.
Security: WordPress core's own ability runner (/wp-abilities/v1/abilities/{name}/run) reached abilities past WPPilot's confirmation gate, write rate limit, preview and design gates and Pro's approval queue. It now runs through the same controls as WPPilot's own REST route; a destructive call without confirm is refused there, and the refusal names the WPPilot route that accepts confirm.
The admin tab bar no longer cuts off its first group when the screen is narrower than the tabs; the groups wrap onto a second centred row.