---
name: elementor-wp-cli-editing
description: "Use only when Elementor backend editing needs WP-CLI/shell access: inspect _elementor_data, clone/reorder JSON containers, write post meta/content with wp commands, clear Elementor caches, and verify live output. Requires SSH/WP-CLI or Niwa main_coding_shell_run with proc_open available."
triggers: []
scope: "general"
version: 2
source: "bundled"
bundle_signature: "elementor-wp-cli-editing-v2-proc-open-gated"
license: MIT
metadata:
  author: Eclips
  hermes:
    tags: [wordpress, elementor, wp-cli, backend-editing, proc-open, cache, verification]
    related_skills: [elementor-niwa-native-editing, osam-rs-wordpress-workflow, web-runtime-ops-playbooks]
---

# Elementor WP-CLI Editing

## Runtime Gate

This is the shell/WP-CLI version of the Elementor editing workflow.

Use it only when one of these is true:

- You are operating over external SSH where `wp` is available.
- Niwa has `main_coding_shell_run` available in the current runtime. That means `proc_open`, `proc_get_status`, and `proc_close` are available.

Do not use this workflow when Niwa omits `main_coding_shell_run`, `main_coding_test_run`, or reports `proc_open_unavailable`. In that runtime, use the bundled `elementor-niwa-native-editing` skill instead. If the task truly requires shell-only work, tell the founder that WP-CLI needs temporary process runtime access through the existing Niwa proc_open on/off route, then return to the native workflow after it is disabled again.

## What This Skill Owns

Use this workflow for structural Elementor edits that are easier to make by reading and rewriting the full `_elementor_data` JSON:

- clone an existing section, container, column, or card;
- reorder top-level or nested Elementor containers;
- regenerate cloned Elementor IDs;
- update `_elementor_data` and `post_content` fallback together;
- run WP-CLI cache clears or file cache cleanup;
- inspect the live HTML and visual layout after the edit.

For simple text, image, link, style, or global-color widget edits, prefer the no-shell native skill even when WP-CLI is available.

## Niwa Shell Invocation

Inside Niwa, run WP-CLI through `main_coding_shell_run`. Do not invent a direct `wp_cli` tool.

Good pattern:

```text
main_coding_shell_run
cwd: /absolute/wordpress/root
command: wp post meta get 974 _elementor_data
```

Use one broad shell command for inspection when possible, then make the smallest write. For multi-line scripts, create a temporary script under `/tmp` or use a quoted PHP/Python one-liner that is safe for the host OS.

## Workflow

### 1. Identify the target page

From SSH or Niwa `main_coding_shell_run`:

```bash
wp post list --post_type=page --fields=ID,post_title,post_name,post_status --format=table
wp db query "SELECT ID,post_title,post_name,post_status FROM wp_posts WHERE post_type='page' ORDER BY post_modified DESC LIMIT 30;"
```

Confirm the public URL and post ID before editing.

### 2. Inspect live output first

Read the cache-busted public URL and note current order, button targets, desktop width, and mobile stacking.

```bash
python3 - <<'PY'
import urllib.request, time
url='https://example.com/page/?_verify=%d' % int(time.time())
s=urllib.request.urlopen(url, timeout=30).read().decode('utf-8', errors='replace')
print('bytes', len(s))
print(s[:500])
PY
```

### 3. Back up both storage layers

```bash
mkdir -p /tmp/elementor-edit-974
wp post get 974 --field=post_content > /tmp/elementor-edit-974/before-post_content.html
wp post meta get 974 _elementor_data > /tmp/elementor-edit-974/before-elementor.json
```

### 4. Parse `_elementor_data` as JSON

Never edit Elementor JSON by raw string concatenation. Decode it, modify arrays/objects, encode it back.

Use a structure printer to find headings, buttons, links, images, and container paths:

```python
import json, subprocess
post_id='974'
raw=subprocess.check_output(['wp','post','meta','get',post_id,'_elementor_data'], text=True)
data=json.loads(raw)

def walk(node, path='root'):
    if isinstance(node, list):
        for i, child in enumerate(node):
            walk(child, f'{path}[{i}]')
        return
    if not isinstance(node, dict):
        return
    st=node.get('settings') or {}
    wt=node.get('widgetType')
    if wt in {'heading','button','text-editor','image'}:
        print(path, 'id=', node.get('id'), 'widget=', wt, 'text=', st.get('title') or st.get('text') or st.get('editor'), 'link=', st.get('link'))
    for i, child in enumerate(node.get('elements') or []):
        walk(child, f'{path}.elements[{i}]')

walk(data)
```

### 5. Clone existing structure instead of inventing styling

When the founder wants another block "like this one", deep-copy the existing container/card and change only content, link, media, and order. Regenerate every cloned Elementor `id`.

```python
import copy, random

used=set()
def collect(x):
    if isinstance(x, dict):
        if x.get('id'):
            used.add(x['id'])
        for child in x.get('elements') or []:
            collect(child)
    elif isinstance(x, list):
        for child in x:
            collect(child)

def new_id():
    while True:
        s=''.join(random.choice('0123456789abcdef') for _ in range(7))
        if s not in used:
            used.add(s)
            return s

def reid(x):
    if isinstance(x, dict):
        if 'id' in x:
            x['id']=new_id()
        for child in x.get('elements') or []:
            reid(child)
    elif isinstance(x, list):
        for child in x:
            reid(child)

collect(data)
clone=copy.deepcopy(data[0])
reid(clone)
```

### 6. Write through WP-CLI

Write full Elementor JSON and fallback content deliberately:

```python
from pathlib import Path
import json, subprocess

elem_tmp=Path('/tmp/elementor-edit-974/after-elementor.json')
html_tmp=Path('/tmp/elementor-edit-974/after-post_content.html')
elem_tmp.write_text(json.dumps(data, ensure_ascii=False, separators=(',', ':')), encoding='utf-8')
html_tmp.write_text(new_content, encoding='utf-8')

subprocess.check_call(['wp','post','meta','update','974','_elementor_data',elem_tmp.read_text(encoding='utf-8')])
subprocess.check_call(['wp','eval',f'$c=file_get_contents("{html_tmp}"); wp_update_post(array("ID"=>974,"post_content"=>$c));'])
```

### 7. Clear Elementor and site caches

```bash
wp post meta delete 974 _elementor_element_cache || true
wp post meta delete 974 _elementor_css || true
wp elementor flush_css || true
wp cache flush || true
rm -rf wp-content/uploads/elementor/css/post-974.css wp-content/cache/* 2>/dev/null || true
```

### 8. Verify

Verify storage, cache-busted HTML, and visual layout:

```bash
wp post meta get 974 _elementor_data | python3 -m json.tool >/dev/null
python3 - <<'PY'
import urllib.request, html, time
url='https://example.com/page/?_verify=%d' % int(time.time())
s=urllib.request.urlopen(url, timeout=30).read().decode('utf-8', errors='replace')
d=html.unescape(s)
print('new text present', 'NEW TEXT' in d)
print('old text still present', 'OLD TEXT' in d)
PY
```

Use browser/mobile visual verification when layout changed.

## Rollback

```bash
wp post meta update 974 _elementor_data "$(cat /tmp/elementor-edit-974/before-elementor.json)"
wp eval '$c=file_get_contents("/tmp/elementor-edit-974/before-post_content.html"); wp_update_post(array("ID"=>974,"post_content"=>$c));'
wp post meta delete 974 _elementor_element_cache || true
wp post meta delete 974 _elementor_css || true
wp elementor flush_css || true
wp cache flush || true
```

## Safety Rules

- Do not duplicate Elementor IDs.
- Do not write invalid JSON.
- Do not update only `post_content` when Elementor renders from `_elementor_data`.
- Do not update only `_elementor_data` when SEO/fallback content should also reflect the change.
- Do not claim success until live output is verified.
- If `main_coding_shell_run` is unavailable, stop using this skill and switch to `elementor-niwa-native-editing`.
