WP Manifestindependent plugin directory
manifest / ai / wp-ai-edit_en

WP AI Edit

AI chat in the WordPress admin that edits the site – English edition (client side of the WP AI Edit / WP Agency Edit pair).

by Weser AI · github.com/livedialai/wp-ai-edit_en · website

0stars
0forks

Install

No release zip yet. The repository archive installs, but the folder name will carry the branch suffix and updates will not flow:

wp plugin install https://github.com/livedialai/wp-ai-edit_en/archive/refs/heads/main.zip

The current version, with its version number, is available under Releases.

AI chat in the WordPress admin that edits the site. It appears as a floating widget in the bottom right of wp-admin — it does not exist on the public site.

Remote Access (Agencies)

An agency instance can operate this site remotely over HTTPS. Access runs through application passwords — a WordPress core feature, not a key system of its own. To WordPress, the caller is a regular user, so all permission checks apply unchanged: an administrator credential has all rights, an editor correspondingly fewer.

Create a credential: Settings → WP AI Edit → Remote Access. The password is shown once — as is standard in WordPress. Any credential can be revoked there at any time; revocation takes effect immediately.

What the agency can query:

GET  /wp-json/wp-ai-edit/v1/auskunft
     identifier, site, WordPress and PHP version, theme, page builder,
     active plugins with versions, number of abilities, permissions of the credential

GET  /wp-json/wp-abilities/v1/abilities
POST /wp-json/wp-abilities/v1/abilities/kiedit/get-page/run
     Run each ability individually (the standard WordPress way for the Abilities API)

Log: Every login and every call is recorded with time, credential name, route, status, and truncated IP in the log under Settings → Remote Access. Suggestions submitted via remote access additionally carry the origin fern.

Remote access can be turned off in the Remote Access tab. Failed login attempts are logged as well.

What it can do

Area Ability
Filling pages create-page, update-page with block markup
Settings set-options (title, tagline, posts per page …)
Plugins install-plugin, toggle-plugin, set-plugin-setting, list-plugins
Design templates fetch-design – reads external pages and extracts colors, fonts, layout
Safety snapshot before every change, rollback to revert
Reading inspect-site – identity, theme, pages, menus, plugins; get-page for the full page content
Small changes replace-text – replaces one passage of text, everything else stays untouched
Images generate-image – create an image from a description, put it in the Media Library
Suggestions list-pending, apply-pending, discard-pending

Structure

wp-ai-edit.php                  Main file: hooks, admin-only, settings
includes/class-abilities.php    18 abilities via the WordPress Abilities API
includes/class-rest.php         REST routes /chat /reset /status /pending
includes/class-inspector.php    Design extraction from external pages
includes/class-workflow.php     Suggestions, preview, apply, discard
includes/class-bild.php         Image generation via WaveSpeed
includes/class-llm.php          OpenAI-compatible client with tool calls
assets/js/widget.js             Chat UI, suggestion cards, image preview
assets/css/widget.css           Speech bubble, panel, cards
prompts/chat.md                 System prompt read-only mode
prompts/editsite.md             System prompt editing mode
docs/funktionen.md              generated reference of all functions
docs/referenz-erheben.php       Collection script
docs/referenz-erzeugen.py       Generator of the reference

Documentation

File Content
README.md Overview, installation, concept, decisions, test results
docs/funktionen.md Complete reference: all 18 abilities with parameters, types, and required fields; REST routes; every setting with its default; database options; all classes with their public methods; hooks; commands; page-builder detection
docs/referenz-erheben.php Collection script, runs in the installation
docs/referenz-erzeugen.py Generates docs/funktionen.md from it

Regenerating the reference

docs/funktionen.md is not written by hand. The data comes from what WordPress actually registers — so that the reference does not go stale while the code moves on:

# 1. Collect the inventory in the installation
cp docs/referenz-erheben.php /tmp/
wp eval-file /tmp/referenz-erheben.php        # writes /tmp/wpaie-doku.json

# 2. Download the JSON and generate the reference
python3 docs/referenz-erzeugen.py <plugin-directory> <path-to-wpaie-doku.json>

The collection script stores its output via the environment variable WPAIE_DATEN, the generator script its target path via WPAIE_ZIEL. Keys are replaced with (gesetzt) in the process — the reference never contains credentials.

Why it does not exist on the site

add_action( 'admin_enqueue_scripts', … );   // admin only
add_action( 'admin_footer', … );            // markup only in the admin
register_rest_route( …, [ 'permission_callback' => 'nur_backend' ] );

No wp_enqueue_scripts, no wp_ajax_nopriv_*, no output in the frontend. Calling the REST route without logging in returns HTTP 401.

Chat commands

Command Effect
/editsite Editing mode: all abilities, editing prompt
/normal Back to read-only mode
/inspect Output the site state as JSON
/reset Clear the conversation history
/hilfe Command overview

Keyboard shortcuts: Ctrl/Cmd + K opens the widget, Esc closes it.

Suggesting and confirming

By default the agent changes nothing directly. A write operation is stored as a suggestion and appears in the chat as a card with three buttons:

  • View preview — opens the suggested version under ?wpaie_vorschau=<id>. For logged-in users only; the live page stays untouched.
  • Go live — applies the change, creating a backup beforehand.
  • Discard — deletes the suggestion, nothing happens.

The agent asks when it is not clear what is wanted:

Should I put this live right away — or would you like to look at it first?

The workflow can be changed under Settings → WP AI Edit → Workflow:

  • Suggest first (default) — every change is presented
  • Apply immediately — changes go live directly; backup and rollback remain

Regardless, the user can say "do it directly" in the chat. Then the model sets direkt: true and that one change is applied without a clarifying question. Conversely, the agent asks first for large rebuilds.

What happens behind the scenes

User: "Change the opening hours to 9-18"
   ↓
kiedit/update-page  (without direkt=true)
   ↓  nothing written
Suggestion 2b2c552b902f + preview link
   ↓  user clicks "View preview"
?wpaie_vorschau=2b2c552b902f  → 403 for outsiders, view for logged-in users
   ↓  user clicks "Go live"
Backup → wp_update_post() → live

The agent can also manage suggestions itself:

  • kiedit/list-pending — open suggestions with IDs
  • kiedit/apply-pending — apply (only after consent)
  • kiedit/discard-pending — discard

Abilities

Ability Effect
kiedit/inspect-site Title, theme, pages, menus, plugin state
kiedit/list-plugins Installed plugins and state
kiedit/fetch-design Read an external page: colors, fonts, layout
kiedit/create-page Create a page (suggestion or direct)
kiedit/update-page Change a page (suggestion or direct)
kiedit/set-options Core options from a whitelist
kiedit/install-plugin Install a plugin from the repository
kiedit/toggle-plugin Activate/deactivate a plugin
kiedit/set-plugin-setting Set a plugin setting
kiedit/snapshot Backup of a state
kiedit/rollback Restore from a backup
kiedit/list-pending Open suggestions
kiedit/apply-pending Apply a suggestion
kiedit/discard-pending Discard a suggestion
kiedit/get-page Read a page's title, status, and full block content
kiedit/replace-text Replace a single passage of text, everything else stays
kiedit/generate-image Create an image from a description, put it in the Media Library
kiedit/image-status Fetch a running image job

Reading pages and changing them individually

The most important principle: update-page replaces the complete page content. Without knowing the current text first, a write attempt would empty the page. That is why the prompt insists: get-page first, then write.

kiedit/get-page     →  title, status, link, full block content, character count,
                       block count, and whether the page is editable through
                       WordPress at all

For typical customer requests — opening hours, prices, phone number, a word in the text — replace-text is the right tool:

{ "id": 61, "suchen": "Our Menu", "ersetzen": "Menu 2026" }

It counts the occurrences, replaces only the first one by default, and leaves everything else untouched. If it cannot find the passage, it stops with a clear notice instead of writing somewhere random. In the test: 1373 → 1371 characters, because the new text was two characters shorter — and Tiramisù, Pizza Marinara, and the rest of the content stayed unchanged.

Both paths go through the suggestion mechanism, not directly to the page.

Pages with Elementor, Divi, and other page builders

Page builders do not store their content in the WordPress content, but in their own fields. A write attempt there would have no effect: the database change happens, but the page looks unchanged.

The plugin detects this and refuses to work instead of failing silently — get-page reports in the bearbeitbar field which page builder is involved:

Page builder Detection marker
Elementor _elementor_edit_mode = builder
Divi _et_pb_use_builder = on
WPBakery / Visual Composer _wpb_vc_js_status = true
Bricks _bricks_page_content_2 filled
Oxygen ct_builder_shortcodes filled

update-page and replace-text then return an error in plain text, and the agent tells the user that this page can only be changed in the page builder itself.

Image generation

Under Settings → WP AI Edit → Image Generation, service, key, model, and size can be entered freely. The default is WaveSpeed with bytedance/seedream-v4.5 (Seedream 4.5, text to image) at 2048×2048.

User: "Take a photo of our pizza for the menu"
   ↓
kiedit/generate-image  →  job to WaveSpeed
   ↓  ~15–25 seconds
Image URL  →  download  →  Media Library (attachment ID)
   ↓
Agent shows the URL and adds it to the page via a suggestion

The key is stored exclusively in the WordPress option and is sent only to the configured service. Other WaveSpeed models can be entered directly, for example bytedance/seedream-v5.0-pro, bytedance/seedream-v4, or wavespeed-ai/z-image/turbo.

Requirements

  • WordPress 6.9+ (Abilities API; tested on 7.1)
  • PHP 8.0+
  • An OpenAI-compatible API with tool calls. Base URL, model, and key are configured in the plugin, not in core.

Default: DeepSeek — base URL https://api.deepseek.com, model deepseek-flash. Freely changeable, works just as well with OpenAI (https://api.openai.com/v1, gpt-4o), Mistral, a custom gateway, or local Ollama.

Installation

From the release (the easy way)

Ready-made package under Releases — it extracts to wp-ai-edit/ and can be uploaded directly. This link stays valid and always points to the latest version:

https://github.com/livedialai/wp-ai-edit_en/releases/latest/download/wp-ai-edit.zip

The same file is also available next to it with the version number in the name (wp-ai-edit-<version>.zip) if you want to pin a specific version.

WordPress → Plugins → Add New → Upload Plugin → select the ZIP → activate.

From the source code

# Copy the plugin folder to wp-content/plugins
wp plugin activate wp-ai-edit

A word of caution about the source ZIP

GitHub's automatic archive (/archive/refs/heads/main.zip) extracts to wp-ai-edit-main/. WordPress would then install the plugin under that name — it works, but the directory is named differently than expected. If you want to avoid that, use the release package or rename it beforehand.

From a running installation

wp plugin install https://github.com/livedialai/wp-ai-edit_en/releases/latest/download/wp-ai-edit.zip --force

Then under Settings → WP AI Edit:

Field Meaning
Base URL without /chat/completions; the path is appended
Model must support tool calls
API key displayed masked; leave empty to keep the old one
Temperature · max tool rounds · timeout freely adjustable
Thinking mode turns on the model's thinking
Test connection sends a test request and shows the response

Below that, the two system prompts as editable text fields. They are pre-filled with a complete Gutenberg reference (block markup, block types, structure) and can be changed directly in the admin. Clearing a field and saving restores the bundled version.

Finally, visibility: at which capability level the widget appears.

How the AI integration works

The plugin talks to /chat/completions in OpenAI format itself — deliberately not through the core AI client, so that base URL, model, and key are freely selectable.

// Tool definitions from the registered abilities
$tools = WP_AI_Edit_LLM::werkzeuge( $ability_names );

// Loop: model responds with tool_calls -> execute ability -> return result
$antwort = WP_AI_Edit_LLM::chat( $messages, $ability_names );

Sequence per round:

  1. POST {base_url}/chat/completions with tools and tool_choice: auto
  2. If the response contains tool_calls, every call is executed via $ability->check_permissions() and $ability->execute()
  3. The result goes back into the conversation as role: tool
  4. Repeat until a text response or max_runden

Ability names are rewritten for the API: kiedit/inspect-sitekiedit_inspect_site, with reverse mapping.

Permissions per mode: in the chat only read-only abilities, in editing mode all. An editor can use the same chat and automatically gets only what their role allows — the check happens per ability in permission_callback.

Design templates from external sites

kiedit/fetch-design loads a public URL and returns compact JSON:

{
  "titel": "…",
  "farben": [ {"wert": "#0a0a0a", "anzahl": 42} ],
  "css_variablen": { "--brand-pink": "335 100% 50%" },
  "schriften": ["Space Grotesk", "Inter"],
  "google_fonts": ["Space Grotesk"],
  "struktur": { "header": 1, "nav": 1, "section": 9, "footer": 1, "grid": 7, "flex": 33, "karten": 19 },
  "ueberschriften": ["H1: …", "H2: …"],
  "cta": ["Start for free →"],
  "bilder_alt": ["…"]
}

Private and reserved IP ranges are blocked so that the server cannot be used as a probe into the internal network.

Tested

test.pizzafamily.site (WP 7.1, PHP 8.2):

Check Result
18 abilities registered
Connection to DeepSeek deepseek-flash ✅ responds
Tool loop (model calls ability, result comes back)
inspect-site, fetch-design on gofonia.de ✅ colors, fonts, layout
create-page with Gutenberg markup ✅ 12 blocks, parsed without errors
Write attempt without direkt=true ✅ suggestion only, page unchanged
Preview link without login ✅ HTTP 403
Preview link with login ✅ renders the new version
Apply suggestion ✅ content changed, suggestion list empty
replace-text character-exact ✅ 1373 → 1371, rest preserved
generate-image ✅ 14 s, 2048×2048, attachment in the Media Library
Image visible in the chat ✅ rendered at 413×413
Page-builder detection (5 systems) ✅ detected, writing refused
Permission check without login ✅ rejected

starfood.pizza (WP 7.1, PHP 8.4, WooCommerce shop, 89 products):

Check Result
Plugin installed and active
18 abilities registered
Frontend untouched (home page, /shop/, /warenkorb/) ✅ HTTP 200, 0 occurrences
Widget in the admin ✅ visible
Live question via the chat ✅ counted 89 dishes, listed 8 active plugins

Limits

  • Not a replacement for a page builder. Elementor, Divi, and relatives build pages visually with instant feedback. A chat is the wrong interface to move a border by three pixels. The tool does not replace the builder, but the call to the agency for small changes to existing pages.
  • Page-builder pages cannot be edited (see above). That is intentional.
  • Design remains manual work. Structurally correct block markup is not the same as good design. The Gutenberg reference in the prompt is more important than the choice of model.

Technical limits of v1

  • The theme on the test installation (pizzafamily) is not a block theme. Design changes therefore take effect through page content, menus, and theme options. At the theme.json level (Global Styles, Templates), the plugin only works with a block theme – the corresponding abilities are missing in v1.
  • The conversation history is embedded into the prompt as text, not as a structured message list. For longer sessions, PromptBuilder::with_history() would be the next step.
  • No streaming output; the response arrives in one piece.
  • install-plugin accepts repository slugs only, not URLs.