@stellisoft/stellify-mcp 0.1.36 → 0.1.38
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/dist/index.js +17 -5
- package/package.json +1 -1
- package/server.json +2 -2
- package/skills/wordpress-import.md +257 -178
package/dist/index.js
CHANGED
|
@@ -600,16 +600,24 @@ Use the returned UUID with html_to_elements (page parameter) or get_route for fu
|
|
|
600
600
|
**For elements inside @foreach loops (SSR/Blade):**
|
|
601
601
|
Use these attributes to reference the loop variable (defaults to \`$item\`):
|
|
602
602
|
- \`textField\`: Field name for text content → outputs \`{{ $item->fieldName }}\`
|
|
603
|
-
- \`hrefField\`: Field name for href → outputs \`href="{{ $item->fieldName }}"\`
|
|
603
|
+
- \`hrefField\`: Field name for href → outputs \`href="{{ $item->fieldName }}"\` (field value ONLY, no prefix)
|
|
604
604
|
- \`srcField\`: Field name for src → outputs \`src="{{ $item->fieldName }}"\`
|
|
605
605
|
|
|
606
|
+
**For hrefs with path prefixes (IMPORTANT):**
|
|
607
|
+
\`hrefField\` outputs ONLY the field value. There is NO \`hrefPrefix\` attribute.
|
|
608
|
+
For links like \`/post/slug-here\`, you MUST use \`hrefExpression\`:
|
|
609
|
+
- \`hrefExpression: "/post/{{ $item->slug }}"\` → outputs \`href="/post/{{ $item->slug }}"\`
|
|
610
|
+
- \`hrefExpression: "/category/{{ $item->slug }}"\` → outputs \`href="/category/{{ $item->slug }}"\`
|
|
611
|
+
|
|
606
612
|
**For complex Blade expressions in attributes:**
|
|
607
|
-
Use expression attributes when you need more than simple field access
|
|
613
|
+
Use expression attributes when you need more than simple field access:
|
|
608
614
|
- \`hrefExpression\`: Blade expression for href → outputs \`href="..."\` with the expression
|
|
609
615
|
- \`srcExpression\`: Blade expression for src → outputs \`src="..."\` with the expression
|
|
610
616
|
- \`altExpression\`: Blade expression for alt → outputs \`alt="..."\` with the expression
|
|
611
617
|
|
|
612
|
-
|
|
618
|
+
Examples:
|
|
619
|
+
- Path prefix: \`hrefExpression: "/post/{{ $item->slug }}"\`
|
|
620
|
+
- Route helper: \`hrefExpression: "{{ route('posts.show', $item->slug) }}"\`
|
|
613
621
|
|
|
614
622
|
**For Blade text content:**
|
|
615
623
|
Use the \`statements\` array with statement UUIDs containing Blade code. The statement's \`code\` property will be output directly for Blade to evaluate.`,
|
|
@@ -739,10 +747,14 @@ For SSR/Blade pages, do NOT pass raw Blade expressions in text or attributes. Th
|
|
|
739
747
|
1. **For static HTML:** Pass clean HTML without Blade syntax, then use \`update_element\` to add dynamic behavior
|
|
740
748
|
2. **For loop content:** After creating elements, use \`update_element\` with:
|
|
741
749
|
- \`textField\`, \`hrefField\`, \`srcField\` for simple field access (outputs \`{{ $item->field }}\`)
|
|
742
|
-
- \`hrefExpression\`, \`srcExpression\`, \`altExpression\` for complex expressions
|
|
750
|
+
- \`hrefExpression\`, \`srcExpression\`, \`altExpression\` for paths with prefixes or complex expressions
|
|
743
751
|
- \`statements\` array with statement UUIDs for text content with Blade code
|
|
744
752
|
3. **For conditionals:** Use \`s-directive\` elements as siblings (see update_element docs)
|
|
745
753
|
|
|
754
|
+
**IMPORTANT - Links with path prefixes:**
|
|
755
|
+
\`hrefField\` outputs ONLY the field value with no prefix. There is NO \`hrefPrefix\` attribute.
|
|
756
|
+
For links like \`/post/my-slug\`, use \`hrefExpression: "/post/{{ $item->slug }}"\` instead.
|
|
757
|
+
|
|
746
758
|
**Loop variable:** Inside \`@foreach\` loops created with \`s-directive\`, the default loop variable is \`$item\`. Use \`textField: "title"\` to output \`{{ $item->title }}\`.
|
|
747
759
|
|
|
748
760
|
Prefer SVG icons over emoji (encoding issues).`,
|
|
@@ -1497,7 +1509,7 @@ const SERVER_INSTRUCTIONS = `Stellify is a coding platform where code is stored
|
|
|
1497
1509
|
// Create MCP server
|
|
1498
1510
|
const server = new Server({
|
|
1499
1511
|
name: 'stellify-mcp',
|
|
1500
|
-
version: '0.1.
|
|
1512
|
+
version: '0.1.38',
|
|
1501
1513
|
}, {
|
|
1502
1514
|
capabilities: {
|
|
1503
1515
|
tools: {},
|
package/package.json
CHANGED
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.
|
|
9
|
+
"version": "0.1.38",
|
|
10
10
|
"packages": [
|
|
11
11
|
{
|
|
12
12
|
"registryType": "npm",
|
|
13
13
|
"identifier": "@stellisoft/stellify-mcp",
|
|
14
|
-
"version": "0.1.
|
|
14
|
+
"version": "0.1.38",
|
|
15
15
|
"transport": {
|
|
16
16
|
"type": "stdio"
|
|
17
17
|
},
|
|
@@ -1,238 +1,317 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: wordpress-import
|
|
3
|
-
description: "Use this skill when importing WordPress sites into Stellify. Invoke with /wordpress-import to
|
|
3
|
+
description: "Use this skill when importing WordPress sites into Stellify. Invoke with /wordpress-import to analyse a WordPress project and generate resources for custom post types, taxonomies, and fields. Assumes standard WordPress models (Post, Page, Category, Tag, User, Media) are already scaffolded via Stellify."
|
|
4
4
|
license: MIT
|
|
5
|
+
icon: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><title>wordpress</title><path d="M3.42,12C3.42,10.76 3.69,9.58 4.16,8.5L8.26,19.72C5.39,18.33 3.42,15.4 3.42,12M17.79,11.57C17.79,12.3 17.5,13.15 17.14,14.34L16.28,17.2L13.18,8L14.16,7.9C14.63,7.84 14.57,7.16 14.11,7.19C14.11,7.19 12.72,7.3 11.82,7.3L9.56,7.19C9.1,7.16 9.05,7.87 9.5,7.9L10.41,8L11.75,11.64L9.87,17.27L6.74,8L7.73,7.9C8.19,7.84 8.13,7.16 7.67,7.19C7.67,7.19 6.28,7.3 5.38,7.3L4.83,7.29C6.37,4.96 9,3.42 12,3.42C14.23,3.42 16.27,4.28 17.79,5.67H17.68C16.84,5.67 16.24,6.4 16.24,7.19C16.24,7.9 16.65,8.5 17.08,9.2C17.41,9.77 17.79,10.5 17.79,11.57M12.15,12.75L14.79,19.97L14.85,20.09C13.96,20.41 13,20.58 12,20.58C11.16,20.58 10.35,20.46 9.58,20.23L12.15,12.75M19.53,7.88C20.2,9.11 20.58,10.5 20.58,12C20.58,15.16 18.86,17.93 16.31,19.41L18.93,11.84C19.42,10.62 19.59,9.64 19.59,8.77L19.53,7.88M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M12,21.54C17.26,21.54 21.54,17.26 21.54,12C21.54,6.74 17.26,2.46 12,2.46C6.74,2.46 2.46,6.74 2.46,12C2.46,17.26 6.74,21.54 12,21.54Z" /></svg>
|
|
5
6
|
metadata:
|
|
6
7
|
author: stellify
|
|
7
8
|
---
|
|
8
9
|
|
|
9
10
|
# WordPress Import Skill
|
|
10
11
|
|
|
11
|
-
You are importing a WordPress site into Stellify
|
|
12
|
+
You are importing a WordPress site into Stellify. This skill focuses on **analysing what's custom** about the WordPress site and generating only those elements. Standard WordPress functionality (Posts, Pages, Categories, Tags, Users, Media) is handled by Stellify's WordPress scaffold.
|
|
12
13
|
|
|
13
|
-
|
|
14
|
+
## Step 1 — Verify WordPress Scaffold
|
|
14
15
|
|
|
15
|
-
|
|
16
|
+
Before proceeding, check that the Stellify WordPress scaffold has been run. Look for the presence of standard WordPress models in the Laravel project:
|
|
16
17
|
|
|
17
|
-
|
|
18
|
+
- `app/Models/Post.php`
|
|
19
|
+
- `app/Models/Page.php`
|
|
20
|
+
- `app/Models/Category.php`
|
|
21
|
+
- `app/Models/Tag.php`
|
|
18
22
|
|
|
19
|
-
|
|
23
|
+
If these don't exist, instruct the user:
|
|
20
24
|
|
|
21
|
-
> **
|
|
25
|
+
> **WordPress scaffold required**
|
|
22
26
|
>
|
|
23
|
-
>
|
|
27
|
+
> Before importing, you need to run the Stellify WordPress scaffold. This creates the standard models, controllers, and routes that every WordPress site uses.
|
|
24
28
|
>
|
|
25
|
-
>
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
## Step 2 — Inventory the Theme
|
|
30
|
-
|
|
31
|
-
Read the full theme directory. Categorise every file:
|
|
32
|
-
|
|
33
|
-
- **Core templates**: index.php, single.php, page.php, archive.php, search.php, 404.php, front-page.php, home.php
|
|
34
|
-
- **Structural partials**: header.php, footer.php, sidebar.php
|
|
35
|
-
- **Template parts**: anything in template-parts/ or patterns/
|
|
36
|
-
- **Functions**: functions.php (and any files it includes/requires)
|
|
37
|
-
- **Styles**: style.css, any CSS/SCSS files
|
|
38
|
-
- **Config**: theme.json if present (block theme configuration)
|
|
39
|
-
|
|
40
|
-
Print the inventory before proceeding.
|
|
29
|
+
> Run: `[scaffold command TBD]`
|
|
30
|
+
>
|
|
31
|
+
> Let me know when this is complete.
|
|
41
32
|
|
|
42
|
-
|
|
33
|
+
Do not proceed until the scaffold is in place.
|
|
43
34
|
|
|
44
|
-
|
|
35
|
+
## Step 2 — Analyse the WordPress Project
|
|
45
36
|
|
|
46
|
-
|
|
47
|
-
- **Taxonomies** registered via `register_taxonomy()` → these become models or enums
|
|
48
|
-
- **Navigation menus** registered via `register_nav_menus()` → these define nav structure
|
|
49
|
-
- **Widget areas** registered via `register_sidebar()` → note for layout
|
|
50
|
-
- **Enqueued scripts/styles** via `wp_enqueue_script/style()` → note any JS dependencies
|
|
51
|
-
- **Custom image sizes** via `add_image_size()` → note for media handling
|
|
52
|
-
- **Theme supports** via `add_theme_support()` → note post formats, thumbnails, etc.
|
|
53
|
-
- **Shortcodes** via `add_shortcode()` → these become Blade includes or components
|
|
54
|
-
- **AJAX handlers** via `wp_ajax_*` → these become API routes
|
|
55
|
-
- **Any included files** → read those too
|
|
37
|
+
Perform a comprehensive analysis of the WordPress site to understand what's custom. This produces a report before any code generation happens.
|
|
56
38
|
|
|
57
|
-
|
|
39
|
+
### 2a. Identify the Active Theme
|
|
58
40
|
|
|
59
|
-
|
|
41
|
+
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
42
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
43
|
+
Note whether it's a classic PHP theme or a block theme (presence of `theme.json` and HTML templates).
|
|
44
|
+
|
|
45
|
+
### 2b. Scan for Custom Post Types & Taxonomies
|
|
46
|
+
|
|
47
|
+
Read `functions.php` and any files it includes/requires. Extract:
|
|
48
|
+
|
|
49
|
+
- **Custom post types** via `register_post_type()` — capture name, labels, supports array, and any custom rewrite rules
|
|
50
|
+
- **Custom taxonomies** via `register_taxonomy()` — capture name, which post types it attaches to, hierarchical setting
|
|
51
|
+
|
|
52
|
+
Also check for CPT plugins that store configuration differently:
|
|
53
|
+
- Custom Post Type UI stores config in `wp_options` under `cptui_post_types` and `cptui_taxonomies`
|
|
54
|
+
- Pods, Toolset, and similar plugins have their own storage patterns
|
|
55
|
+
|
|
56
|
+
### 2c. Scan Plugins Directory
|
|
57
|
+
|
|
58
|
+
List the contents of `wp-content/plugins/` and categorise:
|
|
59
|
+
|
|
60
|
+
**Functionality plugins** (affect what the site does — need attention):
|
|
61
|
+
- WooCommerce — e-commerce (major rebuild)
|
|
62
|
+
- Advanced Custom Fields / ACF Pro — custom fields
|
|
63
|
+
- Gravity Forms / Contact Form 7 / WPForms — forms
|
|
64
|
+
- The Events Calendar — event post type
|
|
65
|
+
- Custom plugin folders (not from wordpress.org)
|
|
66
|
+
|
|
67
|
+
**Low priority** (usually don't need porting):
|
|
68
|
+
- Yoast SEO / Rank Math — SEO meta (can note fields but not critical)
|
|
69
|
+
- Caching plugins (W3 Total Cache, WP Rocket)
|
|
70
|
+
- Security plugins (Wordfence, Sucuri)
|
|
71
|
+
- Backup plugins
|
|
72
|
+
|
|
73
|
+
**Builder plugins** (affect how templates work):
|
|
74
|
+
- Elementor / Elementor Pro
|
|
75
|
+
- WPBakery / Visual Composer
|
|
76
|
+
- Divi Builder
|
|
77
|
+
- Beaver Builder
|
|
78
|
+
|
|
79
|
+
Note: If a builder plugin is present, templates may be stored in the database rather than theme files.
|
|
80
|
+
|
|
81
|
+
### 2d. Database Analysis
|
|
82
|
+
|
|
83
|
+
If database access is available (direct connection or SQL dump), run these diagnostic queries:
|
|
84
|
+
|
|
85
|
+
```sql
|
|
86
|
+
-- Custom post types in use (beyond standard post/page/attachment)
|
|
87
|
+
SELECT post_type, COUNT(*) as count
|
|
88
|
+
FROM wp_posts
|
|
89
|
+
WHERE post_status = 'publish'
|
|
90
|
+
AND post_type NOT IN ('post', 'page', 'attachment', 'revision', 'nav_menu_item', 'wp_template', 'wp_template_part', 'wp_global_styles', 'wp_navigation')
|
|
91
|
+
GROUP BY post_type
|
|
92
|
+
ORDER BY count DESC;
|
|
93
|
+
|
|
94
|
+
-- Custom taxonomies in use (beyond standard category/post_tag)
|
|
95
|
+
SELECT taxonomy, COUNT(*) as term_count
|
|
96
|
+
FROM wp_term_taxonomy
|
|
97
|
+
WHERE taxonomy NOT IN ('category', 'post_tag', 'nav_menu', 'link_category', 'post_format', 'wp_theme', 'wp_template_part_area')
|
|
98
|
+
GROUP BY taxonomy
|
|
99
|
+
ORDER BY term_count DESC;
|
|
100
|
+
|
|
101
|
+
-- Custom meta keys (reveals ACF fields, custom meta)
|
|
102
|
+
-- Excludes WordPress internal keys (prefixed with _)
|
|
103
|
+
SELECT meta_key, COUNT(*) as usage_count
|
|
104
|
+
FROM wp_postmeta
|
|
105
|
+
WHERE meta_key NOT LIKE '\_%'
|
|
106
|
+
GROUP BY meta_key
|
|
107
|
+
ORDER BY usage_count DESC
|
|
108
|
+
LIMIT 30;
|
|
109
|
+
|
|
110
|
+
-- ACF field groups (if ACF is used)
|
|
111
|
+
SELECT post_title, post_name
|
|
112
|
+
FROM wp_posts
|
|
113
|
+
WHERE post_type = 'acf-field-group'
|
|
114
|
+
AND post_status = 'publish';
|
|
115
|
+
|
|
116
|
+
-- Shortcodes in content (indicates embedded functionality)
|
|
117
|
+
SELECT post_type, COUNT(*) as posts_with_shortcodes
|
|
118
|
+
FROM wp_posts
|
|
119
|
+
WHERE post_content REGEXP '\\[[a-zA-Z0-9_-]+.*\\]'
|
|
120
|
+
AND post_status = 'publish'
|
|
121
|
+
GROUP BY post_type;
|
|
122
|
+
|
|
123
|
+
-- Gutenberg blocks in use (for block themes)
|
|
124
|
+
SELECT
|
|
125
|
+
SUBSTRING_INDEX(SUBSTRING_INDEX(post_content, '<!-- wp:', -1), ' ', 1) as block_type,
|
|
126
|
+
COUNT(*) as usage_count
|
|
127
|
+
FROM wp_posts
|
|
128
|
+
WHERE post_content LIKE '%<!-- wp:%'
|
|
129
|
+
AND post_status = 'publish'
|
|
130
|
+
GROUP BY block_type
|
|
131
|
+
ORDER BY usage_count DESC
|
|
132
|
+
LIMIT 20;
|
|
133
|
+
```
|
|
66
134
|
|
|
67
|
-
|
|
135
|
+
### 2e. Produce Analysis Report
|
|
68
136
|
|
|
69
|
-
|
|
137
|
+
Present a structured summary before proceeding:
|
|
70
138
|
|
|
71
|
-
|
|
139
|
+
```
|
|
140
|
+
## WordPress Site Analysis
|
|
141
|
+
|
|
142
|
+
### Theme
|
|
143
|
+
- Name: theme-name
|
|
144
|
+
- Type: Classic PHP / Block theme
|
|
145
|
+
- Template files: [count]
|
|
146
|
+
|
|
147
|
+
### Content Summary
|
|
148
|
+
- Posts: [count]
|
|
149
|
+
- Pages: [count]
|
|
150
|
+
- [custom-type]: [count]
|
|
151
|
+
|
|
152
|
+
### Custom Post Types
|
|
153
|
+
| Name | Slug | Supports | Count |
|
|
154
|
+
|------|------|----------|-------|
|
|
155
|
+
| Portfolio | project | title, editor, thumbnail | 34 |
|
|
156
|
+
| Testimonial | testimonial | title, editor | 12 |
|
|
157
|
+
|
|
158
|
+
### Custom Taxonomies
|
|
159
|
+
| Name | Slug | Attached To | Hierarchical | Terms |
|
|
160
|
+
|------|------|-------------|--------------|-------|
|
|
161
|
+
| Project Type | project_type | project | Yes | 5 |
|
|
162
|
+
|
|
163
|
+
### Custom Fields (non-standard meta keys)
|
|
164
|
+
| Key | Used By | Count |
|
|
165
|
+
|-----|---------|-------|
|
|
166
|
+
| project_client | project | 34 |
|
|
167
|
+
| project_url | project | 34 |
|
|
168
|
+
| testimonial_company | testimonial | 12 |
|
|
169
|
+
|
|
170
|
+
### Plugins Requiring Attention
|
|
171
|
+
- Advanced Custom Fields Pro — field definitions above
|
|
172
|
+
- Contact Form 7 — 2 forms found
|
|
173
|
+
|
|
174
|
+
### Content Flags
|
|
175
|
+
- 15 posts contain shortcodes (will need processing during content migration)
|
|
176
|
+
- Page builder detected: Elementor (some layouts stored in database)
|
|
177
|
+
|
|
178
|
+
### Generation Plan
|
|
179
|
+
**Will generate:**
|
|
180
|
+
- Model: Project (custom post type)
|
|
181
|
+
- Model: Testimonial (custom post type)
|
|
182
|
+
- Model: ProjectType (custom taxonomy)
|
|
183
|
+
- Add fields to models: client, url, company
|
|
184
|
+
|
|
185
|
+
**Standard (already scaffolded):**
|
|
186
|
+
- Post, Page, Category, Tag, User, Media
|
|
187
|
+
|
|
188
|
+
**Manual attention needed:**
|
|
189
|
+
- Contact forms (2) — recreate in Laravel
|
|
190
|
+
- Shortcodes in content — process during migration
|
|
191
|
+
- Elementor layouts — extract design intent from theme templates
|
|
192
|
+
```
|
|
72
193
|
|
|
73
|
-
|
|
194
|
+
After presenting the report, ask:
|
|
74
195
|
|
|
75
|
-
|
|
196
|
+
> Does this analysis look correct? Ready to proceed with generating the custom resources?
|
|
76
197
|
|
|
77
|
-
|
|
198
|
+
## Step 3 — Generate Custom Resources
|
|
78
199
|
|
|
79
|
-
|
|
80
|
-
// Example Post model
|
|
81
|
-
class Post extends Model {
|
|
82
|
-
protected $table = 'wp_posts';
|
|
83
|
-
protected $primaryKey = 'ID';
|
|
84
|
-
|
|
85
|
-
// Scope to only published posts (not revisions, drafts, etc.)
|
|
86
|
-
public function scopePublished($query) {
|
|
87
|
-
return $query->where('post_status', 'publish')
|
|
88
|
-
->where('post_type', 'post');
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
```
|
|
200
|
+
For each custom post type and taxonomy identified, create the necessary Laravel resources using Stellify MCP tools.
|
|
92
201
|
|
|
93
|
-
|
|
94
|
-
- Posts & Pages → `wp_posts` (distinguished by `post_type` column: 'post', 'page', or custom types)
|
|
95
|
-
- Categories & Tags → `wp_terms` + `wp_term_taxonomy` + `wp_term_relationships`
|
|
96
|
-
- Users → `wp_users`
|
|
97
|
-
- Post meta / custom fields → `wp_postmeta`
|
|
98
|
-
- Comments → `wp_comments`
|
|
99
|
-
- Navigation menus → `wp_posts` with `post_type = 'nav_menu_item'` + `wp_terms` with taxonomy `nav_menu`
|
|
202
|
+
### 3a. Custom Post Type Models
|
|
100
203
|
|
|
101
|
-
|
|
102
|
-
- `$post->post_title` (not `$post->title`)
|
|
103
|
-
- `$post->post_content` (not `$post->content`)
|
|
104
|
-
- `$post->post_excerpt` (not `$post->excerpt`)
|
|
105
|
-
- `$post->post_name` (this is the slug)
|
|
106
|
-
- `$post->post_date` (not `$post->published_at`)
|
|
107
|
-
- `$post->post_status` (not `$post->status`)
|
|
204
|
+
For each custom post type, use `create_resources` with `api: false`:
|
|
108
205
|
|
|
109
|
-
**
|
|
206
|
+
**Model requirements:**
|
|
207
|
+
- Table name matching the post type slug (e.g., `projects`, `testimonials`)
|
|
208
|
+
- Standard fields: `title`, `slug`, `content`, `excerpt`, `status`, `published_at`, `author_id`
|
|
209
|
+
- Custom fields identified from meta analysis
|
|
210
|
+
- `getRouteKeyName()` returning `'slug'` for URL-friendly routing
|
|
211
|
+
- Relationships to custom taxonomies
|
|
110
212
|
|
|
111
|
-
|
|
213
|
+
**Migration requirements:**
|
|
214
|
+
- All standard fields plus custom fields
|
|
215
|
+
- Foreign keys to users table for author
|
|
216
|
+
- Indexes on slug, status, published_at
|
|
112
217
|
|
|
113
|
-
|
|
218
|
+
**Controller requirements:**
|
|
219
|
+
- `index()` returning paginated published items
|
|
220
|
+
- `show($model)` returning single item
|
|
221
|
+
- Return arrays (Stellify convention), not views or JSON
|
|
114
222
|
|
|
115
|
-
|
|
223
|
+
Example model structure:
|
|
116
224
|
|
|
117
225
|
```php
|
|
118
|
-
|
|
119
|
-
public function index(): array
|
|
226
|
+
class Project extends Model
|
|
120
227
|
{
|
|
121
|
-
|
|
122
|
-
|
|
228
|
+
protected $fillable = [
|
|
229
|
+
'title', 'slug', 'content', 'excerpt',
|
|
230
|
+
'status', 'published_at', 'author_id',
|
|
231
|
+
'client', 'url', // custom fields
|
|
232
|
+
];
|
|
233
|
+
|
|
234
|
+
protected $casts = [
|
|
235
|
+
'published_at' => 'datetime',
|
|
236
|
+
];
|
|
237
|
+
|
|
238
|
+
public function getRouteKeyName(): string
|
|
239
|
+
{
|
|
240
|
+
return 'slug';
|
|
241
|
+
}
|
|
123
242
|
|
|
124
|
-
public function
|
|
125
|
-
{
|
|
126
|
-
|
|
127
|
-
}
|
|
243
|
+
public function author(): BelongsTo
|
|
244
|
+
{
|
|
245
|
+
return $this->belongsTo(User::class, 'author_id');
|
|
246
|
+
}
|
|
128
247
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
}
|
|
248
|
+
public function projectTypes(): BelongsToMany
|
|
249
|
+
{
|
|
250
|
+
return $this->belongsToMany(ProjectType::class);
|
|
251
|
+
}
|
|
134
252
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
253
|
+
public function scopePublished($query)
|
|
254
|
+
{
|
|
255
|
+
return $query->where('status', 'published');
|
|
256
|
+
}
|
|
139
257
|
}
|
|
140
258
|
```
|
|
141
259
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
### 5c. Create Routes
|
|
145
|
-
Create route entries mapping URLs to controller methods.
|
|
146
|
-
|
|
147
|
-
### 5d. Create the Layout (layouts/app.blade.php)
|
|
148
|
-
Read header.php and footer.php. Create a single Blade layout that combines:
|
|
149
|
-
- The HTML structure from header.php (doctype, head, opening body, nav)
|
|
150
|
-
- `@yield('content')` for page content
|
|
151
|
-
- The structure from footer.php (closing elements, footer content)
|
|
260
|
+
### 3b. Custom Taxonomy Models
|
|
152
261
|
|
|
153
|
-
|
|
154
|
-
- `wp_head()` → `@vite(['resources/css/app.css', 'resources/js/app.js'])` and `<meta>` tags
|
|
155
|
-
- `wp_footer()` → nothing needed (Vite handles it)
|
|
156
|
-
- `wp_nav_menu()` → `@include('partials.nav')`
|
|
157
|
-
- `bloginfo('name')` → `{{ config('app.name') }}`
|
|
158
|
-
- `bloginfo('description')` → `{{ config('app.description', '') }}`
|
|
159
|
-
- `body_class()` → appropriate Tailwind classes
|
|
160
|
-
- `language_attributes()` → `lang="{{ str_replace('_', '-', app()->getLocale()) }}"`
|
|
262
|
+
For each custom taxonomy:
|
|
161
263
|
|
|
162
|
-
|
|
163
|
-
|
|
264
|
+
**Model requirements:**
|
|
265
|
+
- Table name matching taxonomy slug (e.g., `project_types`)
|
|
266
|
+
- Fields: `name`, `slug`, `description`, `parent_id` (if hierarchical)
|
|
267
|
+
- `getRouteKeyName()` returning `'slug'`
|
|
268
|
+
- Relationship to associated post types via pivot table
|
|
164
269
|
|
|
165
|
-
|
|
270
|
+
**Migration requirements:**
|
|
271
|
+
- Main taxonomy table
|
|
272
|
+
- Pivot table linking to each associated post type (e.g., `project_project_type`)
|
|
166
273
|
|
|
167
|
-
|
|
274
|
+
### 3c. Routes
|
|
168
275
|
|
|
169
|
-
|
|
276
|
+
Create routes following WordPress URL conventions where sensible:
|
|
170
277
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
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):**
|
|
278
|
+
```php
|
|
279
|
+
// Custom post type archives and singles
|
|
280
|
+
Route::get('/projects', [ProjectController::class, 'index'])->name('projects.index');
|
|
281
|
+
Route::get('/projects/{project}', [ProjectController::class, 'show'])->name('projects.show');
|
|
195
282
|
|
|
196
|
-
|
|
283
|
+
// Custom taxonomy archives
|
|
284
|
+
Route::get('/project-type/{projectType}', [ProjectTypeController::class, 'show'])->name('project-types.show');
|
|
285
|
+
```
|
|
197
286
|
|
|
198
|
-
|
|
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`
|
|
287
|
+
## Step 4 — Document Manual Tasks
|
|
208
288
|
|
|
209
|
-
|
|
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
|
-
```
|
|
289
|
+
Create a summary of items requiring manual attention:
|
|
215
290
|
|
|
216
|
-
###
|
|
217
|
-
|
|
291
|
+
### Forms
|
|
292
|
+
List each form found with fields and purpose. The user will need to recreate these using Laravel form handling or a package like Laravel Livewire.
|
|
218
293
|
|
|
219
|
-
###
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
- WordPress post grid → `<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">`
|
|
294
|
+
### Shortcodes in Content
|
|
295
|
+
List shortcodes found and their apparent purpose. These will need to be:
|
|
296
|
+
1. Processed during content migration (find/replace with HTML)
|
|
297
|
+
2. Or converted to Blade components if they need to remain dynamic
|
|
224
298
|
|
|
225
|
-
|
|
299
|
+
### Plugin Functionality
|
|
300
|
+
For significant plugins (WooCommerce, membership plugins, booking systems), note that this functionality needs separate planning and is outside the scope of this import.
|
|
226
301
|
|
|
227
|
-
|
|
302
|
+
### Content Migration
|
|
303
|
+
Note that content migration is a separate step. The user will need to:
|
|
304
|
+
1. Export content from WordPress (WP-CLI, plugin, or direct SQL)
|
|
305
|
+
2. Transform data to match new schema
|
|
306
|
+
3. Import into Laravel database
|
|
307
|
+
4. Process/clean embedded shortcodes
|
|
308
|
+
5. Migrate media files and update URLs
|
|
228
309
|
|
|
229
310
|
## Important Rules
|
|
230
311
|
|
|
231
|
-
- **
|
|
232
|
-
- **
|
|
233
|
-
- **
|
|
234
|
-
- **
|
|
235
|
-
- **
|
|
236
|
-
- **
|
|
237
|
-
- **Be pragmatic** — focus on the core templates that define the site's main pages. Skip hyper-specific template variations unless they're clearly important.
|
|
238
|
-
- **Content handling depends on database mode** — In Mode A (existing DB), the content is already there via the WordPress tables. In Mode B (fresh DB), content is separate and can be migrated later.
|
|
312
|
+
- **Analysis first** — Always complete the full analysis before generating any code
|
|
313
|
+
- **Only generate custom elements** — Standard WordPress models come from the scaffold
|
|
314
|
+
- **Fresh database only** — This skill assumes a clean Laravel database, not connecting to WordPress tables
|
|
315
|
+
- **No template conversion** — Visual design/styling is handled by a separate skill
|
|
316
|
+
- **Document unknowns** — Flag anything that can't be automatically converted
|
|
317
|
+
- **Be pragmatic** — Focus on post types and taxonomies actually in use (have content), not just registered
|