Skip to content

feat(cli): WordPress site generator for Studio Code (theme + companion plugin)#3666

Draft
draganescu wants to merge 19 commits into
trunkfrom
feat/wordpress-site-generator
Draft

feat(cli): WordPress site generator for Studio Code (theme + companion plugin)#3666
draganescu wants to merge 19 commits into
trunkfrom
feat/wordpress-site-generator

Conversation

@draganescu

@draganescu draganescu commented Jun 2, 2026

Copy link
Copy Markdown

Related issues

  • No related issue linked yet.

How AI was used in this PR

This is an AI-assisted proof of concept for a larger Studio Code capability. AI helped generate the initial implementation, iterate on the generation pipeline, add focused tests, and resolve the latest origin/trunk merge conflicts. The current branch state has been reviewed locally enough for draft review, but this should still be treated as directional work rather than merge-ready production code.

Proposed Changes

What

Adds a WordPress site generator flow to Studio Code so a user can describe a site and have Studio produce a working local WordPress site from that brief. The flow can plan design directions, generate a block theme, add a companion plugin for behavior and custom data, seed pages and content, generate imagery, and validate the result.

Why

The goal is to make Studio more useful for first-draft site creation. Instead of asking the agent to hand-edit one file at a time, this gives it a purpose-built path for creating a coherent starting point that still follows WordPress conventions: editable block content, a presentation-focused theme, and behavior that survives a theme switch.

How

The branch adds a site-generator skill, generation helpers, agent tools, tests, and an eval harness around the workflow. The generated site is split into a block theme plus a companion plugin, with identifiers and manifest checks to keep generated templates, blocks, custom post types, taxonomies, and seeded content aligned.

The current state also includes fixes from live iteration:

  • Guards against custom post type routing issues.
  • Avoids archive recursion in generated templates.
  • Skips dangling taxonomy references.
  • Avoids relying on remote fonts.
  • Uses a single-pool orchestration path for faster generation.
  • Adds eval coverage for generated plugin and content contracts.

This PR is current with origin/trunk as of eb7ce03d via merge commit 11a08945.

Testing Instructions

Verification already run on the updated branch:

  • ./node_modules/.bin/eslint --fix apps/cli/ai/skills.ts apps/cli/ai/system-prompt.ts
  • corepack npm run typecheck --workspace wp-studio
  • ./node_modules/.bin/tsc -p scripts/tsconfig.json --noEmit
  • ./node_modules/.bin/vitest run apps/cli/ai/tests/system-prompt.test.ts apps/cli/ai/tests/skill-prompts-contract.test.ts

Suggested reviewer flow from a complete dependency install:

npm install
npm run cli:build
node apps/cli/dist/cli/main.mjs ai

Then authenticate with WordPress.com if needed and exercise the /site-generator flow against a throwaway local site.

Note: a full all-workspaces typecheck was attempted from the new worktree, but that worktree did not have its own dependency install. The CLI workspace typecheck and the root scripts TypeScript check passed.

Pre-merge Checklist

  • Have you checked for TypeScript, React or other console errors?

…tion for studio ai

Adds an end-to-end WordPress site generator to the `studio ai`/`studio code`
agent: generation tools plus knowledge/runbook skills that produce a
pure-presentation block theme and a companion plugin (CPTs, REST routes,
build-less plain-JS blocks), seed content into the live DB, and fill AI imagery.

Tools (registered in tools/index.ts): generate_design_previews, generate_theme
(parallel per-file generation), generate_companion_plugin, seed_content,
generate_image. Orchestrated by the `site-generator` skill (/site-generator
slash command) alongside theme-architecture, companion-plugin, layout-patterns,
wp-best-practices, and data-persistence knowledge skills + bundled generator
prompt fragments adapted from Telex.

Generation core (apps/cli/ai/generation/): non-streaming parallel Anthropic
client reusing the wpcom provider env, transient-error retry/backoff, robust
JSON extraction, manifest schema, AI_IMAGE handling, path/WP-CLI helpers.

Key correctness fixes from live testing:
- seed_content writes large post content to a host file the WP filesystem reads
  (wp post create/update <file>) instead of a giant --post_content arg that the
  daemon IPC bus truncates (25KB -> 16 bytes).
- CPT entries generated with their meta fields from the companion plugin's post
  types, so collections actually populate.
- AI images use the authorized OpenAI images route (gpt-image-1 under the
  studio-assistant feature slug); Telex's Imagen slug is 403 for Studio
  accounts. Placeholders are stripped when not logged into WordPress.com.

Adds unit tests (apps/cli/ai/tests/wsg-generation.test.ts, 22 passing).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@draganescu

Copy link
Copy Markdown
Author

This for now is a one size exploration which tries to settle the best shape of porting some of the Telex learnings on site generation into agent friendly skills and tools.

Images don't generate because of the auth system on WPCOM for certain ai proxies which is not tied to user auth. The agent using the orchestrator command is still significantly slower than the Telex PHP orchestrator.

Finally, an area this exploration shed light on is that we need to have a wp build system bundled somehow so we don't npm install for every theme fresh. Right now the draft instructs generation as vanilla JS for blocks to avoid building.

@youknowriad

Copy link
Copy Markdown
Contributor

Thanks for starting this, excited for some consolidation to happen. a few questions:

  • how is generate_theme different than the current scaffold_theme (should they merge?)
  • Some sites might not need plugins at all, do we always attach a plugin or only when needed?
  • Do we need to use the slash command to generate a site, can't it be auto-invoked on user's prompt?

Reading your comment, it seems you're considering this as a PoC for the whole flow. I wonder how possible would it be to ship the corrections and each step separately as iterations. Maybe you're already considering this?

@draganescu

Copy link
Copy Markdown
Author

Yes I am considering this a PoC and I'll push it as far as it gets until I get a site that just works from a prompt. Then we figure out details. Because once we have a working path we can architect it into pieces. That is non standard, I know, but these skills are very finicky to test standalone. Ideally, studio code will get what Telex didn't - an eval system - so we can confidently tweak these.

To your questions:

  • no, ideally we don't need to use the slash command, it should auto invoke when user says "make a website", "build a theme" etc.

  • the plugins are blocks, content model and content seeding. I think the orchestrator skill should give the user the option to opt out of any.

  • theme generation vs theme scaffolding, one is a generation of a full theme the other is a setup for someone to build on top of. Yes, they should merge.

@youknowriad

Copy link
Copy Markdown
Contributor

Most sites I think don't need a plugin (like mine) and would prefer just having core blocks. I think we can discuss these details later, but I'd prefer that we don't optimize for plugin heavy outputs.

draganescu and others added 12 commits June 3, 2026 18:10
…ce A)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…idate (slice D)

Pins custom post type, block, and REST identifiers to a single canonical
themePrefix derived from the manifest, then reconciles generated markup to it
and validates no reference resolves to nothing registered. Fixes custom blocks
that did not render and Query Loops pointed at non-existent post types.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…e B)

Image generation in resolveAiImagesInHtml was fully serial; it now runs through
a bounded pool (injectable generator for testing), cutting the seed stage ~83s
on the restaurant case with no rate-limit failures. Generation runPooled caps
raised 4/5/3 -> 8 (theme files, plugin blocks, seed pages, CPT entries); these
help only phases with many similar-duration calls — per-phase wall-clock is
still gated by the single longest call (style.css, main plugin PHP), so the
larger win remains a single-pool merge (slice G).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Custom blocks are now authored as JSX/React under blocks/<slug>/src/ (editor
imports @wordpress/*; front-end view.js stays plain DOM) and compiled in-process
to build/ via esbuild — WordPress packages externalised to their wp.* runtime
globals with a generated index.asset.php, the same output shape as
@wordpress/scripts but no npm install, no webpack, no subprocess. The plugin
registers from build/. Verified end-to-end: a generated reservation-form block
compiles and renders on the live site. Reverses the prior build-less plain-JS
mandate so Studio's developer-centric users get real React blocks.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ice C)

Replaces the per-item seed loop (post list + create/update + a call per meta key
+ option writes — tens of serial WP-CLI round-trips through the PHP-WASM daemon)
with ONE wp eval-file pass: a fixed PHP seeder reads a generated _seed-manifest.json
plus each item's content file and upserts every post/page/CPT entry, sets meta,
and the static front page in a single WP load (Telex's content-loader pattern).
Verified: content inserts, the menu CPT query populates, the reservation block
renders, the front page is set; the seed stage's DB-write tax is eliminated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…s (slice D follow-up)

The harness validated content references against the manifest but not whether the
companion plugin's PHP actually registers those post types — a register_post_type
drift would orphan seeded entries while every content reference stayed canonical.
findRegisteredPostTypes extracts the plugin's literal register_post_type keys
(multi-line tolerant); the harness now reports cptUnreg for any manifest CPT the
PHP does not register. Verified against real generated output.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…n (slice G)

Adds generate_site: theme + companion plugin + page/block/CPT content generation
run as ONE parallel pool (orchestrate.ts), then a strict deferred-write sequence
(theme write+activate -> plugin write+compile+activate -> single-pass seed). The
two long-pole calls (style.css ~14k, main plugin PHP ~12k) now overlap the cheap
fan-out instead of gating three serial phases. On restaurant-reservations the
generation phase drops from ~280-300s (theme+plugin+seed) to a single 91s stage;
total run ~430s -> 229s. Correctness unchanged: idViolations 0, cptUnreg 0, the
JSX block compiles + renders, the menu CPT populates, the front page is set.

The three legacy tools are unchanged except for export additions and remain
available for manual/granular use; SKILL.md collapses steps 4-6 to one. The eval
harness gains a --merged mode.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…g when plugin fails

Adversarial review of the merged orchestration found two failure-path defects the
happy run did not exercise:
- Theme tasks (theme.json/style.css/parts/templates) were unguarded, so one
  runGenerator throw rejected the whole pool and discarded the already-generated
  plugin + content. Each theme file is now guarded and routed to generationFailed;
  theme activation is gated on style.css actually being produced.
- A failed main plugin PHP still seeded CPT entries under a post type the unwritten
  plugin never registered (orphaned rows). CPT items are now dropped from seeding
  when the plugin failed, with an explicit summary instead of a misleading one.

Happy path unchanged (the guards/filters are identity on success). Adds unit
coverage for theme-failure routing.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… recursion, dangling taxonomy, and remote fonts

Four real bugs surfaced by a studio code generation, fixed the way Telex avoids them — a deterministic guard plus an aligned generator prompt for each:

- CPT archive/page routing collision: sanitizeCptArchiveSlugs() rewrites any has_archive/rewrite slug equal to a page slug so the archive stays at the theme-prefixed base and the page keeps its URL.
- Archive-loop recursion (runaway-height pages): port Telex's no-post-content-in-loop / finite-perPage / no-nested-query rules into template.md; add findArchiveLoopViolations() linter.
- Unregistered-taxonomy empty query: reconcileMarkup strips an unknown taxQuery so the loop falls back to postType-only (non-empty); validateMarkup flags unknown_taxonomy_reference; prompts steer facets to post_meta.
- Remote/file fonts in theme.json: stripRemoteFontFaces() plus token-only fontFamily system stacks in theme-json.md; aligned the contradicting font docs.

Eval harness gains four scorecard checks so each class regresses loudly.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…generator

# Conflicts:
#	apps/cli/ai/skills.ts
#	apps/cli/ai/system-prompt.ts
@draganescu

Copy link
Copy Markdown
Author

Site generation skills and tools

This branch intentionally keeps the site-generation surface inside ai/skills and ai/tools. The agent still owns conversation, review, and repair loops; the site generator is a tool-owned engine behind generate_site.

Full generation flow

flowchart TD
    A["User asks for a complete site"] --> B["Pi runtime starts Studio agent turn"]
    B --> C["Skill tool loads site-generator"]
    C --> D["site-spec resolves missing site details"]
    D --> E{"Use existing site"}
    E -->|"Yes"| F["site_info or list_sites"]
    E -->|"No"| G["create_site"]
    F --> H["start_site if needed"]
    G --> H
    H --> I["visual-design plus theme-architecture build spec"]
    I --> J["generate_design_previews"]
    J --> K{"User choice"}
    K -->|"Choose direction"| L["generate_site"]
    K -->|"Regenerate"| J
    K -->|"Stop"| Z["No site changes"]
    L --> M{"Review before apply"}
    M -->|"Yes"| N["guided mode apply false"]
    N --> O["Return manifest warnings STAGED_RUN_ID"]
    O --> P{"User approves"}
    P -->|"No"| I
    P -->|"Yes"| Q["generate_site stagedRunId apply true"]
    M -->|"No"| R["one-shot mode"]
    R --> S["Plan generate validate apply"]
    Q --> S
    S --> T["generate_image if needed"]
    T --> U["validate_and_fix_blocks"]
    U --> V["take_screenshot"]
    V --> W{"Rendered issues"}
    W -->|"Yes"| X["visual-polish inspect_design Edit or wp_cli"]
    X --> U
    W -->|"No"| Y["Optional audits preview export sync"]
Loading

Skills

Skill Purpose When used in full generation Can user choice skip it
site-generator Main workflow contract for complete local site generation. Always for "build me a site" or "generate a theme/site" requests. No, unless the user is not asking for full generation.
site-spec Gathers site name, target site, and layout preference before creation. When the prompt lacks site name or the agent needs to create/select a site. Yes, if the user already named the site and target.
visual-design Defines visual direction, tone, typography, color, and layout quality. Before previews and whenever visual polish is needed. Mostly no for polished generation; can be minimal if the user gives a strict design brief.
theme-architecture Keeps the theme pure presentation and defines layout/content mode. During spec planning and manifest generation. No for full generation.
block-content Rules for editable WordPress block markup and validation. During generated content review and block fixes. No for any block markup changes.
layout-patterns Concrete block-theme layout patterns and gotchas. Conditional when the chosen design needs specific layout patterns. Yes, if the design is simple.
companion-plugin Defines generated plugin behavior, CPTs, REST routes, and custom blocks. Conditional when the spec needs behavior beyond presentation. Yes, if the site is brochure-only.
data-persistence Canonical pattern for forms and submitted data. Conditional for contact, booking, RSVP, lead capture, reviews, or similar inputs. Yes, if there is no submitted data.
wp-best-practices PHP quality, escaping, sanitization, capabilities, and performance rules. Conditional when companion-plugin PHP is generated or reviewed. Yes, if no plugin PHP is involved.
plugin-recommendations Chooses third-party plugins/blocks when core WordPress is not enough. Conditional for ecommerce, LMS, events, forms, galleries, SEO/perf plugin choices. Yes, if the generated site sticks to core + companion plugin.
visual-polish Screenshot-driven rendered-DOM diagnosis and fix loop. After generation if screenshots show layout, spacing, contrast, width, or interaction issues. Yes, if the user stops after generation or screenshots are acceptable.
annotate User points at rendered elements and leaves visual annotations. Optional after screenshots when feedback is easier by pointing. Yes, always optional.
need-for-speed Frontend performance audit and optimization recommendations. Optional after the site works visually. Yes, always optional.
rank-me-up On-page SEO audit and search recommendations. Optional after content and structure exist. Yes, always optional.
taxonomist Category taxonomy cleanup for content-heavy existing sites. Not part of new-site generation unless the user asks for taxonomy work. Yes, always optional for generation.
wpcom-remote-management Remote WordPress.com site operations via API. Not used for local full generation; used for remote sites. Yes for local generation.

Registered Studio tools

Tool Purpose When used in full generation Can user choice skip it
create_site Creates a local Studio site. When the user wants a new site. Yes, if using an existing site.
list_sites Lists local sites. When target site is ambiguous. Yes, if the target is explicit.
site_info Reads details for a target site. When using or confirming an existing site. Yes, if context is already enough.
start_site Starts the local WordPress site. Before WP CLI, screenshots, activation, or seeding if stopped. Sometimes, if already running.
stop_site Stops the site. Cleanup only. Yes.
delete_site Deletes a local site. Cleanup or failed experiment removal only. Yes.
wp_cli Runs WP CLI commands against the site. Conditional for manual fixes, checks, or post-generation repair. Yes, if generator and validation succeed without manual fixes.
scaffold_theme Creates a manual theme scaffold. Not normal full generation; useful for manual theme work. Yes.
generate_design_previews Generates first-fold design directions and opens them. Normal pre-generation review step. User can ask to skip previews, but quality is usually better with them.
generate_site Public facade over SiteGenerationEngine. Plans, generates, validates, stages/applies. Central tool for full generation. No for this workflow. User can choose guided vs one-shot.
generate_image Fills remaining theme-level AI_IMAGE placeholders. After apply when templates/parts still need imagery. Yes, if images are disabled, unavailable, or not needed.
validate_html_blocks Validates block HTML. Spot checks for generated or edited block markup. Sometimes, if validate_and_fix_blocks covers the needed path.
validate_and_fix_blocks Validates and repairs invalid block markup. Normal post-generation QA. Should not be skipped for complete generation.
take_screenshot Captures rendered site screenshots. Normal visual QA after apply. Should not be skipped for complete generation.
inspect_design Inspects rendered DOM/CSS to diagnose visual issues. Conditional when screenshots reveal issues. Yes, if screenshots look good.
create_preview Creates a hosted preview site. Optional sharing/review flow after local generation. Yes.
list_previews Lists preview sites. Optional preview management. Yes.
update_preview Updates a preview site. Optional preview management. Yes.
delete_preview Deletes a preview site. Optional preview cleanup. Yes.
share_screenshot Shares screenshots in remote-session contexts. Optional for remote review flows. Yes.
install_taxonomy_scripts Installs taxonomy helper scripts. Taxonomist workflow only. Yes for site generation.
audit_performance Runs performance audit. Optional final QA. Yes.
audit_seo Runs SEO audit. Optional final QA. Yes.
list_connected_remote_sites Lists connected remote sites. Optional sync/publish workflow. Yes.
push_site Pushes local site to remote. Optional publishing/sync workflow. Yes.
pull_site Pulls remote site into local. Optional sync workflow before generation or repair. Yes.
import_site Imports a site archive. Optional setup path. Yes.
export_site Exports local site. Optional backup/handoff path. Yes.
open_annotation_browser Opens annotation UI. Optional user feedback loop. Yes.
wait_for_annotations Waits for annotation feedback. Optional user feedback loop. Yes.
studio_present Presents rich chat artifacts when artifact emission is enabled. Optional desktop/artifact handoff. Yes.

Runtime tools also involved

Runtime tool Purpose When used Can user choice skip it
Skill Loads skill instructions such as site-generator and visual-design. Normal generation routing. No for skill-driven generation.
AskUserQuestion Presents structured choices to the user. Site choice, design choice, guided approval, regenerate/stop decisions. Yes, if the prompt is explicit enough for one-shot mode.
Read, Write, Edit, Bash, Grep, Glob, Ls Pi coding-agent file and shell tools scoped to Studio sites. Inspection and manual repair after generated output. Yes, if no manual repair is needed.
wpcom_request Remote WordPress.com API tool. Remote-site mode only. Yes for local generation.

Internal modules behind generate_site

These live under apps/cli/ai/tools/site-generator. They are not separate user-facing tools; they are implementation modules owned by the tool layer.

Module Responsibility
orchestrate.ts SiteGenerationEngine phases, guided staging, one-shot apply, summaries.
manifest.ts Manifest schema and normalization.
generators.ts Prompt loading and model calls for theme/plugin/content artifacts.
llm.ts Pooled non-streaming generation client, retries, cancellation/progress support.
identifier-contract.ts Canonical block/CPT/REST identifiers and reconciliation.
build-block.ts Custom block build compilation helpers.
images.ts AI_IMAGE placeholder resolution in HTML.
wpcom-image.ts Image generation proxy integration.
paths.ts Slug derivation and path containment helpers.
seed-php.ts Seeder PHP builder and result parser.
site-wp.ts Site daemon and WP CLI helpers.
theme-guards.ts Theme safety checks such as remote font and archive-loop guards.

User-choice skip points

  • Existing site selected: skip site-spec detail gathering and create_site.
  • User chooses "regenerate" at design review: repeat generate_design_previews, skip generation for that preview.
  • User chooses "stop" at design review: no site files or DB changes.
  • User asks for review before applying: use generate_site guided mode and skip apply until approval.
  • User rejects guided result: keep staged artifacts but do not apply; revise spec/design and regenerate.
  • User approves guided result: apply exact staged artifacts with stagedRunId; skip regeneration.
  • User asks for one-shot or does not need review: skip guided staging and apply immediately.
  • User disables imagery or is not logged in: skip image generation and strip/defer placeholders.
  • Screenshots look good: skip inspect_design and manual repair.
  • User does not need sharing, audits, remote sync, import/export, annotations, SEO, or performance checks: skip those optional tools.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants