@stellisoft/stellify-mcp 0.1.35 → 0.1.36

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -374,12 +374,80 @@ Create a new UI element. Provide either `page` (route UUID) for root elements, o
374
374
  **Parameters:**
375
375
  - `type` (required): Element type - one of:
376
376
  - HTML5: `s-wrapper`, `s-input`, `s-form`, `s-svg`, `s-shape`, `s-media`, `s-iframe`
377
- - Components: `s-loop`, `s-transition`, `s-freestyle`, `s-motion`
377
+ - Components: `s-transition`, `s-freestyle`, `s-motion`
378
378
  - Blade: `s-directive`
379
379
  - Shadcn/ui: `s-chart`, `s-table`, `s-combobox`, `s-accordion`, `s-calendar`, `s-contiguous`
380
380
  - `page` (optional): UUID of the page/route (for root elements)
381
381
  - `parent` (optional): UUID of the parent element (for child elements)
382
382
 
383
+ **Using `s-directive` for Blade Conditionals:**
384
+
385
+ `s-directive` elements output Blade directives (like `@if`, `@foreach`, `@endif`). They are **sibling elements** — they don't wrap children. To conditionally render content:
386
+
387
+ 1. Create an `s-directive` element with a statement for the opening directive (e.g., `@if(...)`)
388
+ 2. Create the content element(s) as the **next sibling(s)**
389
+ 3. Create another `s-directive` element with a statement for the closing directive (e.g., `@endif`)
390
+
391
+ Example — conditionally showing an image:
392
+ ```
393
+ // 1. Create statement for @if
394
+ create_statement_with_code({
395
+ file: "<file-uuid>",
396
+ code: "@if($item->featured_image)"
397
+ })
398
+
399
+ // 2. Create opening directive element and set its statement
400
+ create_element({ type: "s-directive", page: "<route-uuid>" })
401
+ update_element({ uuid: "<if-directive-uuid>", data: { "statement": "<if-statement-uuid>" } })
402
+
403
+ // 3. Create the image as the next sibling
404
+ html_to_elements({ page: "<route-uuid>", elements: "<img class=\"w-full\" />" })
405
+ // Then update with dynamic src:
406
+ update_element({ uuid: "<img-uuid>", data: { "srcField": "featured_image" } })
407
+
408
+ // 4. Create statement for @endif
409
+ create_statement_with_code({ file: "<file-uuid>", code: "@endif" })
410
+
411
+ // 5. Create closing directive element
412
+ create_element({ type: "s-directive", page: "<route-uuid>" })
413
+ update_element({ uuid: "<endif-directive-uuid>", data: { "statement": "<endif-statement-uuid>" } })
414
+ ```
415
+
416
+ The three elements render in order as siblings:
417
+ ```blade
418
+ @if($item->featured_image)
419
+ <img class="w-full" src="{{ $item->featured_image }}" />
420
+ @endif
421
+ ```
422
+
423
+ **Using `s-directive` for Loops:**
424
+
425
+ ```
426
+ // 1. Create @foreach directive
427
+ create_statement_with_code({ file: "<file-uuid>", code: "@foreach($posts as $item)" })
428
+ create_element({ type: "s-directive", page: "<route-uuid>" })
429
+ update_element({ uuid: "<foreach-uuid>", data: { "statement": "<foreach-statement-uuid>" } })
430
+
431
+ // 2. Create loop content (article with dynamic fields)
432
+ html_to_elements({ page: "<route-uuid>", elements: "<article><h2></h2><p></p></article>" })
433
+ // Update elements to use loop item fields:
434
+ update_element({ uuid: "<h2-uuid>", data: { "textField": "title" } }) // → {{ $item->title }}
435
+ update_element({ uuid: "<p-uuid>", data: { "textField": "excerpt" } }) // → {{ $item->excerpt }}
436
+
437
+ // 3. Create @endforeach directive
438
+ create_statement_with_code({ file: "<file-uuid>", code: "@endforeach" })
439
+ create_element({ type: "s-directive", page: "<route-uuid>" })
440
+ update_element({ uuid: "<endforeach-uuid>", data: { "statement": "<endforeach-statement-uuid>" } })
441
+ ```
442
+
443
+ **Loop Item Attributes:**
444
+ Inside `@foreach` loops, use these attributes on elements to reference `$item`:
445
+ - `textField: "fieldName"` → outputs `{{ $item->fieldName }}`
446
+ - `hrefField: "fieldName"` → outputs `href="{{ $item->fieldName }}"`
447
+ - `srcField: "fieldName"` → outputs `src="{{ $item->fieldName }}"`
448
+ - `hrefExpression: "{{ route('posts.show', $item->slug) }}"` → for complex expressions
449
+ - `srcExpression`, `altExpression` → same pattern for other attributes
450
+
383
451
  ---
384
452
 
385
453
  #### `update_element`
@@ -397,7 +465,18 @@ Update an existing UI element.
397
465
  - `locked`: Prevent editing (boolean)
398
466
  - `tag`: HTML tag (div, input, button, etc.)
399
467
  - `classes`: CSS classes array `["class1", "class2"]`
400
- - `text`: Element text content
468
+ - `text`: Static text content
469
+ - `statements`: Array of statement UUIDs for dynamic Blade content
470
+
471
+ **Loop item fields** (for elements inside `@foreach` loops, references `$item`):
472
+ - `textField`: Field name → outputs `{{ $item->fieldName }}`
473
+ - `hrefField`: Field name → outputs `href="{{ $item->fieldName }}"`
474
+ - `srcField`: Field name → outputs `src="{{ $item->fieldName }}"`
475
+
476
+ **Expression attributes** (for complex Blade expressions):
477
+ - `hrefExpression`: Full Blade expression for href (e.g., `"{{ route('posts.show', $item->slug) }}"`)
478
+ - `srcExpression`: Full Blade expression for src
479
+ - `altExpression`: Full Blade expression for alt
401
480
 
402
481
  **Event handlers** (set value to method UUID):
403
482
  - `click`: @click
@@ -480,11 +559,6 @@ html_to_elements(page: routeUUID, elements: "<main>...</main>")
480
559
  html_to_elements(page: routeUUID, elements: "<footer>...</footer>")
481
560
  ```
482
561
 
483
- **Alternative approach (wrap in a single container):**
484
- ```
485
- html_to_elements(page: routeUUID, elements: "<div class='min-h-screen flex flex-col'><header>...</header><main>...</main><footer>...</footer></div>")
486
- ```
487
-
488
562
  **Features:**
489
563
  - Parses HTML structure
490
564
  - Creates all elements with proper nesting
package/dist/index.js CHANGED
@@ -569,16 +569,15 @@ Use the returned UUID with html_to_elements (page parameter) or get_route for fu
569
569
  },
570
570
  {
571
571
  name: 'create_element',
572
- description: `Create a UI element. Provide page (route UUID) for root elements, or parent (element UUID) for children. Use s-loop for v-for elements.`,
572
+ description: `Create a UI element. Provide page (route UUID) for root elements, or parent (element UUID) for children.`,
573
573
  inputSchema: {
574
574
  type: 'object',
575
575
  properties: {
576
576
  type: {
577
577
  type: 'string',
578
578
  enum: [
579
- 's-wrapper', 's-input', 's-form', 's-svg', 's-shape', 's-media', 's-iframe',
580
- 's-loop', 's-transition', 's-freestyle', 's-motion',
581
- 's-directive'
579
+ 's-wrapper', 's-input', 's-form', 's-svg', 's-shape', 's-media',
580
+ 's-freestyle', 's-directive'
582
581
  ],
583
582
  description: 'Element type - must be one of the valid Stellify element types',
584
583
  },
@@ -596,7 +595,24 @@ Use the returned UUID with html_to_elements (page parameter) or get_route for fu
596
595
  },
597
596
  {
598
597
  name: 'update_element',
599
- description: `Update a UI element. Data object: tag, classes, text, event handlers (method UUIDs), classBindings. Set 'name' on root elements to create Blade views (e.g., name="notes.index" for view('notes.index')).`,
598
+ description: `Update a UI element. Data object: tag, classes, text, event handlers (method UUIDs), classBindings. Set 'name' on root elements to create Blade views (e.g., name="notes.index" for view('notes.index')).
599
+
600
+ **For elements inside @foreach loops (SSR/Blade):**
601
+ Use these attributes to reference the loop variable (defaults to \`$item\`):
602
+ - \`textField\`: Field name for text content → outputs \`{{ $item->fieldName }}\`
603
+ - \`hrefField\`: Field name for href → outputs \`href="{{ $item->fieldName }}"\`
604
+ - \`srcField\`: Field name for src → outputs \`src="{{ $item->fieldName }}"\`
605
+
606
+ **For complex Blade expressions in attributes:**
607
+ Use expression attributes when you need more than simple field access (e.g., route helpers, method calls):
608
+ - \`hrefExpression\`: Blade expression for href → outputs \`href="..."\` with the expression
609
+ - \`srcExpression\`: Blade expression for src → outputs \`src="..."\` with the expression
610
+ - \`altExpression\`: Blade expression for alt → outputs \`alt="..."\` with the expression
611
+
612
+ Example: \`hrefExpression: "{{ route('posts.show', $item->slug) }}"\`
613
+
614
+ **For Blade text content:**
615
+ Use the \`statements\` array with statement UUIDs containing Blade code. The statement's \`code\` property will be output directly for Blade to evaluate.`,
600
616
  inputSchema: {
601
617
  type: 'object',
602
618
  properties: {
@@ -718,12 +734,16 @@ When HTML contains multiple root-level elements (e.g., <header>, <main>, <footer
718
734
  **@click auto-wiring:** Pass 'file' UUID to auto-resolve @click="methodName" handlers. Methods must exist in the file first.
719
735
 
720
736
  **Blade Syntax Handling:**
721
- When element text or attributes contain Blade syntax like \`{{ $post->title }}\` or \`{!! $post->content !!}\`, do NOT store them as literal text. Instead:
722
- - Parse the Blade expression and create a statement/clause for it
723
- - Store the statement UUID in the element's \`statements\` array
724
- - The SSR assembler will evaluate these at render time
737
+ For SSR/Blade pages, do NOT pass raw Blade expressions in text or attributes. The HTML parser stores them literally which causes rendering issues. Instead:
738
+
739
+ 1. **For static HTML:** Pass clean HTML without Blade syntax, then use \`update_element\` to add dynamic behavior
740
+ 2. **For loop content:** After creating elements, use \`update_element\` with:
741
+ - \`textField\`, \`hrefField\`, \`srcField\` for simple field access (outputs \`{{ $item->field }}\`)
742
+ - \`hrefExpression\`, \`srcExpression\`, \`altExpression\` for complex expressions
743
+ - \`statements\` array with statement UUIDs for text content with Blade code
744
+ 3. **For conditionals:** Use \`s-directive\` elements as siblings (see update_element docs)
725
745
 
726
- For example, \`{{ $post->title }}\` should become a statement that references the \`$post\` variable and accesses its \`title\` property.
746
+ **Loop variable:** Inside \`@foreach\` loops created with \`s-directive\`, the default loop variable is \`$item\`. Use \`textField: "title"\` to output \`{{ $item->title }}\`.
727
747
 
728
748
  Prefer SVG icons over emoji (encoding issues).`,
729
749
  inputSchema: {
@@ -1477,7 +1497,7 @@ const SERVER_INSTRUCTIONS = `Stellify is a coding platform where code is stored
1477
1497
  // Create MCP server
1478
1498
  const server = new Server({
1479
1499
  name: 'stellify-mcp',
1480
- version: '0.1.35',
1500
+ version: '0.1.36',
1481
1501
  }, {
1482
1502
  capabilities: {
1483
1503
  tools: {},
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stellisoft/stellify-mcp",
3
- "version": "0.1.35",
3
+ "version": "0.1.36",
4
4
  "mcpName": "io.github.MattStellisoft/stellify-mcp",
5
5
  "description": "MCP server for Stellify - AI-native code generation platform",
6
6
  "main": "dist/index.js",
package/server.json CHANGED
@@ -6,12 +6,12 @@
6
6
  "url": "https://github.com/Stellify-Software-Ltd/stellify-mcp",
7
7
  "source": "github"
8
8
  },
9
- "version": "0.1.35",
9
+ "version": "0.1.36",
10
10
  "packages": [
11
11
  {
12
12
  "registryType": "npm",
13
13
  "identifier": "@stellisoft/stellify-mcp",
14
- "version": "0.1.35",
14
+ "version": "0.1.36",
15
15
  "transport": {
16
16
  "type": "stdio"
17
17
  },
@@ -12,10 +12,6 @@ You are importing a WordPress site into Stellify by reading its PHP theme files
12
12
 
13
13
  **Match the rendering approach of the WordPress site.** If content is server-rendered in WordPress (which most of it will be — posts, pages, archives, navigation), create Blade templates. If a feature uses JavaScript for client-side interactivity (AJAX forms, dynamic filtering, live search, infinite scroll, modals, sliders), create a Vue component for that specific piece. The default is Blade — only reach for Vue when the WordPress source is doing something that genuinely requires JS.
14
14
 
15
- ## Step 0 — Discover Tools
16
-
17
- List all available Stellify MCP tools. Understand what each tool does, what parameters it requires, and any dependencies between them (e.g. you need a file before you can add methods to it, you need a method before you can add statements). Print a summary of the tools and their creation order.
18
-
19
15
  ## Step 1 — Identify the Active Theme
20
16
 
21
17
  Look in `./wp-content/themes/` to find the active theme. If there are multiple themes, check for the most recently modified one, or the one with the most template files. If unsure, ask which theme to import.
@@ -60,60 +56,13 @@ Read functions.php thoroughly. Extract:
60
56
 
61
57
  ## Step 4 — Plan the Laravel Structure
62
58
 
63
- Before calling any Stellify tools, write out the full plan:
64
-
65
- ### Models
66
- - Post (always — WordPress core)
67
- - Page (always — WordPress core)
68
- - Category, Tag (if used)
69
- - Any custom post types found in Step 3
70
- - User (if the theme has author pages)
71
-
72
- ### Controllers
73
- - PostController (index, show) — handles blog listing and single posts
74
- - PageController (show) — handles static pages
75
- - One controller per custom post type
76
- - HomeController — if front-page.php or a static homepage exists
77
- - SearchController — if search.php exists
78
-
79
- ### Routes (web.php)
80
- Map the WordPress URL structure:
81
- - `/` → HomeController or PostController@index
82
- - `/blog` or `/posts` → PostController@index (if static homepage)
83
- - `/{post-slug}` → PostController@show
84
- - `/{page-slug}` → PageController@show
85
- - `/category/{slug}` → PostController@index (filtered)
86
- - `/tag/{slug}` → PostController@index (filtered)
87
- - `/search` → SearchController@index
88
- - Custom post type archives and singles
89
-
90
- ### Blade Views
91
- Everything is Blade. The directory structure should be:
92
- ```
93
- layouts/
94
- app.blade.php ← from header.php + footer.php
95
- posts/
96
- index.blade.php ← from archive.php or index.php
97
- show.blade.php ← from single.php
98
- pages/
99
- show.blade.php ← from page.php
100
- partials/
101
- nav.blade.php ← from wp_nav_menu output
102
- sidebar.blade.php ← from sidebar.php
103
- post-card.blade.php ← from template-parts/content.php
104
- comments.blade.php ← from comments.php
105
- search-form.blade.php ← from searchform.php
106
- errors/
107
- 404.blade.php ← from 404.php
108
- ```
109
-
110
- ### Migrations (Mode B only)
111
- If the user chose Mode B (fresh database), plan one migration per model with fields derived from:
112
- - WordPress core fields: title, slug, content, excerpt, featured_image, status, published_at
113
- - Custom fields from `get_post_meta()` calls found in templates
114
- - Any ACF or custom meta boxes registered in functions.php
59
+ Before calling any Stellify tools, plan:
115
60
 
116
- If the user chose Mode A (existing database), skip migrations entirely the tables already exist.
61
+ - **Models:** One per post type (Post, Page, plus any custom types from Step 3)
62
+ - **Controllers:** One per model with index/show actions
63
+ - **Routes:** Match WordPress URL structure (archive at `/`, singles at `/{slug}`, taxonomies at `/category/{slug}`)
64
+ - **Views:** Mirror WordPress template hierarchy — layout from header+footer, index/show views per post type, partials from template-parts
65
+ - **Migrations (Mode B only):** One per model with fields from WordPress core + any custom fields found in templates
117
66
 
118
67
  ## Step 5 — Build in Stellify (Order of Operations)
119
68
 
@@ -211,52 +160,58 @@ Translate WordPress functions to Blade:
211
160
  - `language_attributes()` → `lang="{{ str_replace('_', '-', app()->getLocale()) }}"`
212
161
 
213
162
  ### 5e. Create Blade Views
214
- For each WordPress template file, read the PHP and create the equivalent Blade view. **All views use `@extends('layouts.app')` and `@section('content')`.**
163
+ For each WordPress template file, read the PHP and create the equivalent Blade view using `html_to_elements`. **All views use `@extends('layouts.app')` and `@section('content')`.**
164
+
165
+ **⚠️ Multiple Root Elements:** When converting HTML with multiple root-level elements (e.g., `<header>`, `<main>`, `<footer>`), only the first root element gets attached to the route. Make separate `html_to_elements` calls for each root element.
215
166
 
216
167
  **WordPress → Blade translation guide:**
217
168
 
218
- The Blade column depends on which database mode was chosen. Mode A uses WordPress column names directly; Mode B uses clean Laravel names.
219
-
220
- | WordPress | Blade (Mode A existing DB) | Blade (Mode B fresh DB) |
221
- |-----------|------------------------------|---------------------------|
222
- | `the_title()` | `{{ $post->post_title }}` | `{{ $post->title }}` |
223
- | `the_content()` | `{!! $post->post_content !!}` | `{!! $post->content !!}` |
224
- | `the_excerpt()` | `{{ $post->post_excerpt }}` | `{{ $post->excerpt }}` |
225
- | `the_permalink()` | `{{ route('posts.show', $post->post_name) }}` | `{{ route('posts.show', $post->slug) }}` |
226
- | `the_post_thumbnail()` | via `wp_postmeta` lookup | `<img src="{{ asset('storage/' . $post->featured_image) }}" />` |
227
- | `the_date()` / `the_time()` | `{{ \Carbon\Carbon::parse($post->post_date)->format('d M Y') }}` | `{{ $post->published_at->format('d M Y') }}` |
228
- | `the_author()` | `{{ $post->author->display_name }}` | `{{ $post->author->name }}` |
229
- | `comments_template()` | `@include('partials.comments', ['post' => $post])` | same |
230
- | `get_template_part('content')` | `@include('partials.post-card', ['post' => $post])` | same |
231
- | `have_posts() / the_post()` | Use an `s-loop` element (see below) | same |
232
- | `wp_link_pages()` | `{{ $posts->links() }}` | same |
233
- | `get_search_form()` | `@include('partials.search-form')` | same |
234
- | `wp_nav_menu()` | `@include('partials.nav')` | same |
235
- | `get_header()` / `get_footer()` | handled by `@extends('layouts.app')` | same |
236
- | `esc_html()` / `esc_attr()` | `{{ }}` (Blade auto-escapes) | same |
237
- | `wp_kses_post()` | `{!! !!}` (for trusted HTML content) | same |
238
-
239
- **Stellify `s-loop` elements for iteration:**
240
-
241
- WordPress's "The Loop" (`have_posts() / the_post()`) requires two things in Stellify:
242
-
243
- 1. **Set the element type to `s-loop`** using `update_element` with `type: "s-loop"` and `variable` set to the collection name from the controller's return array. This tells Stellify to iterate over that variable and pass each item as `$item` to child elements.
244
-
245
- 2. **Write the `@foreach` in the Blade template as well.** The Blade content inside the element still needs the `@foreach` loop.
246
-
247
- Example: If the controller returns `['posts' => Post::paginate(10)]`, the element that lists posts should be updated to:
248
- ```json
249
- {
250
- "uuid": "<element-uuid>",
251
- "data": {
252
- "type": "s-loop",
253
- "variable": "posts"
254
- }
255
- }
256
- ```
257
- And the Blade content inside uses `@foreach($posts as $post)` ... `@endforeach` as normal.
169
+ Mode A uses WordPress column names; Mode B uses clean Laravel names.
170
+
171
+ **IMPORTANT:** Inside `@foreach` loops, use `$item` as the loop variable. This matches the Stellify assembler's expectations for `textField`, `hrefField`, `srcField` attributes.
258
172
 
259
- This applies to any listing post archives, category pages, search results, related posts, comment lists, navigation menu items, etc.
173
+ | WordPress | Blade (Mode A) | Blade (Mode B) | Stellify Attribute |
174
+ |-----------|----------------|----------------|-------------------|
175
+ | `the_title()` | `{{ $item->post_title }}` | `{{ $item->title }}` | `textField: "title"` |
176
+ | `the_content()` | `{!! $item->post_content !!}` | `{!! $item->content !!}` | statement with code |
177
+ | `the_excerpt()` | `{{ $item->post_excerpt }}` | `{{ $item->excerpt }}` | `textField: "excerpt"` |
178
+ | `the_permalink()` | `{{ route('posts.show', $item->post_name) }}` | `{{ route('posts.show', $item->slug) }}` | `hrefExpression: "..."` |
179
+ | `the_date()` | `{{ $item->post_date->format('d M Y') }}` | `{{ $item->published_at->format('d M Y') }}` | statement with code |
180
+ | `the_author()` | `{{ $item->author->display_name }}` | `{{ $item->author->name }}` | statement with code |
181
+ | `get_template_part()` | `@include('partials.post-card', ['post' => $item])` | same | s-directive |
182
+ | `have_posts()` | `@foreach($posts as $item)` | same | s-directive pair |
183
+
184
+ **Conditional Rendering with `s-directive`:**
185
+
186
+ WordPress blocks that render conditionally (like `<!-- wp:post-featured-image -->`) use `s-directive` elements. See the MCP tool documentation for the sibling pattern — create an opening directive, content elements, then a closing directive as siblings.
187
+
188
+ Common WordPress conditionals to convert (use `$item` inside loops):
189
+ - `<!-- wp:post-featured-image -->` → `@if($item->featured_image)` ... `@endif`
190
+ - `<!-- wp:post-excerpt -->` → `@if($item->post_excerpt)` ... `@endif`
191
+ - `<!-- wp:post-comments -->` → `@if($item->comments->count() > 0)` ... `@endif`
192
+ - `<!-- wp:query-no-results -->` → `@if($posts->isEmpty())` ... `@endif` (outside loop)
193
+
194
+ **Iteration (The Loop):**
195
+
196
+ WordPress's "The Loop" (`have_posts() / the_post()`) maps to `@foreach` directives. Use `s-directive` elements for the opening `@foreach` and closing `@endforeach`.
197
+
198
+ **IMPORTANT - Loop Variable:** The default loop variable is `$item`. When creating elements inside a loop:
199
+ 1. Do NOT pass raw Blade syntax to `html_to_elements` — it will be stored literally
200
+ 2. Create clean HTML first, then use `update_element` to add dynamic attributes:
201
+ - `textField: "title"` → outputs `{{ $item->title }}`
202
+ - `hrefField: "slug"` → outputs `href="{{ $item->slug }}"`
203
+ - `srcField: "featured_image"` → outputs `src="{{ $item->featured_image }}"`
204
+ 3. For complex expressions (route helpers, method calls), use:
205
+ - `hrefExpression: "{{ route('posts.show', $item->slug) }}"`
206
+ - `srcExpression: "{{ $item->featured_image }}"`
207
+ 4. For text with Blade code, create a statement with `create_statement_with_code`, then add its UUID to the element's `statements` array via `update_element`
208
+
209
+ Example loop structure using `s-directive` siblings:
210
+ ```
211
+ 1. s-directive with statement: "@foreach($posts as $item)"
212
+ 2. article element (content to repeat)
213
+ 3. s-directive with statement: "@endforeach"
214
+ ```
260
215
 
261
216
  ### 5f. Create Partials & Components
262
217
  Convert template-parts/ files into Blade partials using `@include`. Static partials receive data via the second argument: `@include('partials.post-card', ['post' => $post])`. If a template part relies on JavaScript for interactivity (e.g. a slider, a filterable gallery, a live search form), create a Vue component instead and ensure it is registered and mounted in `app.js`.
@@ -267,18 +222,9 @@ Do not try to port WordPress CSS. Read the visual intent from the theme's CSS/th
267
222
  - WordPress navigation → `<nav class="flex items-center gap-6">`
268
223
  - WordPress post grid → `<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">`
269
224
 
270
- ## Step 6 — Review
271
-
272
- After building everything, print a summary:
273
- - Database mode chosen (A or B)
274
- - Total models created (and which WordPress tables they map to, if Mode A)
275
- - Total controllers and methods
276
- - Total routes
277
- - Total Blade views and partials
278
- - Total Vue components (if any) and why each was needed
279
- - Any WordPress features that could NOT be mapped (e.g. specific plugins, shortcodes with no equivalent)
280
- - If Mode B: provide a SQL migration query or artisan command to copy content from the WordPress database to the new schema
281
- - Suggestions for manual follow-up
225
+ ## Step 6 — Review (Optional)
226
+
227
+ If requested, summarize what was created and note any WordPress features that couldn't be mapped.
282
228
 
283
229
  ## Important Rules
284
230