@stellisoft/stellify-mcp 0.1.37 → 0.1.39
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 +61 -1
- package/dist/stellify-client.d.ts +6 -0
- package/dist/stellify-client.js +5 -0
- package/package.json +1 -1
- package/server.json +2 -2
- package/skills/wordpress-import.md +253 -200
package/dist/index.js
CHANGED
|
@@ -131,6 +131,11 @@ Pass 'includes' array for framework class dependencies (auto-resolved to UUIDs).
|
|
|
131
131
|
type: 'string',
|
|
132
132
|
description: 'Optional module name to group this file with related code (e.g., "blog-posts", "user-auth"). Module is auto-created if it doesn\'t exist.',
|
|
133
133
|
},
|
|
134
|
+
attributes: {
|
|
135
|
+
type: 'array',
|
|
136
|
+
items: { type: 'string' },
|
|
137
|
+
description: 'PHP 8 class-level attributes (e.g., ["Fillable([\'name\', \'email\'])"], ["ObservedBy(UserObserver::class)"]). Use search_attributes tool to find available attributes.',
|
|
138
|
+
},
|
|
134
139
|
},
|
|
135
140
|
required: ['directory', 'name', 'type'],
|
|
136
141
|
},
|
|
@@ -206,6 +211,11 @@ Pass 'includes' array for framework class dependencies (auto-resolved to UUIDs).
|
|
|
206
211
|
description: 'Context: Design decisions',
|
|
207
212
|
items: { type: 'string' },
|
|
208
213
|
},
|
|
214
|
+
attributes: {
|
|
215
|
+
type: 'array',
|
|
216
|
+
description: 'PHP 8 attributes for the method (e.g., ["Route(\\"/api/users\\")"], ["Middleware(\\"auth\\")"]). Use search_attributes tool to find available attributes.',
|
|
217
|
+
items: { type: 'string' },
|
|
218
|
+
},
|
|
209
219
|
},
|
|
210
220
|
required: ['file', 'name'],
|
|
211
221
|
},
|
|
@@ -322,6 +332,11 @@ For significant changes, include context fields: summary, rationale, references,
|
|
|
322
332
|
description: 'Context: Design decisions made',
|
|
323
333
|
items: { type: 'string' },
|
|
324
334
|
},
|
|
335
|
+
attributes: {
|
|
336
|
+
type: 'array',
|
|
337
|
+
description: 'PHP 8 attributes for the method (e.g., ["Route(\\"/api/users\\")"], ["Middleware(\\"auth\\")"]). Use search_attributes tool to find available attributes.',
|
|
338
|
+
items: { type: 'string' },
|
|
339
|
+
},
|
|
325
340
|
},
|
|
326
341
|
required: ['uuid'],
|
|
327
342
|
},
|
|
@@ -343,6 +358,32 @@ For significant changes, include context fields: summary, rationale, references,
|
|
|
343
358
|
},
|
|
344
359
|
},
|
|
345
360
|
},
|
|
361
|
+
{
|
|
362
|
+
name: 'search_attributes',
|
|
363
|
+
description: `Search for available PHP 8 attributes in Laravel. Returns attribute suggestions with descriptions, namespaces, targets (class/method/property/parameter), and expected arguments.
|
|
364
|
+
|
|
365
|
+
Use this before adding attributes to files or methods to find the correct attribute name and syntax.
|
|
366
|
+
|
|
367
|
+
Example queries:
|
|
368
|
+
- "fillable" → finds Fillable attribute for models
|
|
369
|
+
- "middleware" → finds Middleware attribute for controllers
|
|
370
|
+
- "queue" → finds Queue, Tries, Timeout attributes for jobs`,
|
|
371
|
+
inputSchema: {
|
|
372
|
+
type: 'object',
|
|
373
|
+
properties: {
|
|
374
|
+
query: {
|
|
375
|
+
type: 'string',
|
|
376
|
+
description: 'Search term to match against attribute names (e.g., "fillable", "middleware", "tries")',
|
|
377
|
+
},
|
|
378
|
+
target: {
|
|
379
|
+
type: 'string',
|
|
380
|
+
enum: ['class', 'method', 'property', 'parameter'],
|
|
381
|
+
description: 'Filter attributes by where they can be applied. Default: returns all.',
|
|
382
|
+
},
|
|
383
|
+
},
|
|
384
|
+
required: ['query'],
|
|
385
|
+
},
|
|
386
|
+
},
|
|
346
387
|
{
|
|
347
388
|
name: 'delete_method',
|
|
348
389
|
description: 'Delete a method from a file by UUID. This permanently removes the method and all its code. Requires both the file UUID and method UUID.',
|
|
@@ -994,6 +1035,11 @@ Required: uuid, name, type. For significant changes, include context fields: sum
|
|
|
994
1035
|
description: 'Context: Design decisions',
|
|
995
1036
|
items: { type: 'string' },
|
|
996
1037
|
},
|
|
1038
|
+
attributes: {
|
|
1039
|
+
type: 'array',
|
|
1040
|
+
items: { type: 'string' },
|
|
1041
|
+
description: 'PHP 8 class-level attributes (e.g., ["Fillable([\'name\', \'email\'])"], ["ObservedBy(UserObserver::class)"]). Use search_attributes tool to find available attributes.',
|
|
1042
|
+
},
|
|
997
1043
|
},
|
|
998
1044
|
required: ['uuid', 'name', 'type'],
|
|
999
1045
|
},
|
|
@@ -1509,7 +1555,7 @@ const SERVER_INSTRUCTIONS = `Stellify is a coding platform where code is stored
|
|
|
1509
1555
|
// Create MCP server
|
|
1510
1556
|
const server = new Server({
|
|
1511
1557
|
name: 'stellify-mcp',
|
|
1512
|
-
version: '0.1.
|
|
1558
|
+
version: '0.1.39',
|
|
1513
1559
|
}, {
|
|
1514
1560
|
capabilities: {
|
|
1515
1561
|
tools: {},
|
|
@@ -1666,6 +1712,20 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1666
1712
|
],
|
|
1667
1713
|
};
|
|
1668
1714
|
}
|
|
1715
|
+
case 'search_attributes': {
|
|
1716
|
+
const result = await stellify.searchAttributes(args);
|
|
1717
|
+
return {
|
|
1718
|
+
content: [
|
|
1719
|
+
{
|
|
1720
|
+
type: 'text',
|
|
1721
|
+
text: JSON.stringify({
|
|
1722
|
+
success: true,
|
|
1723
|
+
...result,
|
|
1724
|
+
}, null, 2),
|
|
1725
|
+
},
|
|
1726
|
+
],
|
|
1727
|
+
};
|
|
1728
|
+
}
|
|
1669
1729
|
case 'delete_method': {
|
|
1670
1730
|
const { file, uuid } = args;
|
|
1671
1731
|
const result = await stellify.deleteMethod(file, uuid);
|
|
@@ -11,6 +11,7 @@ export interface CreateFileParams {
|
|
|
11
11
|
extension?: string;
|
|
12
12
|
code?: string;
|
|
13
13
|
auto_create_dependencies?: boolean;
|
|
14
|
+
attributes?: string[];
|
|
14
15
|
}
|
|
15
16
|
export interface CreateMethodParams {
|
|
16
17
|
file: string;
|
|
@@ -27,6 +28,7 @@ export interface CreateMethodParams {
|
|
|
27
28
|
value?: string;
|
|
28
29
|
}>;
|
|
29
30
|
body?: string;
|
|
31
|
+
attributes?: string[];
|
|
30
32
|
}
|
|
31
33
|
export interface AddMethodBodyParams {
|
|
32
34
|
file: string;
|
|
@@ -198,4 +200,8 @@ export declare class StellifyClient {
|
|
|
198
200
|
}): Promise<any>;
|
|
199
201
|
listPatterns(): Promise<any>;
|
|
200
202
|
getAssembledCode(uuid: string): Promise<any>;
|
|
203
|
+
searchAttributes(params: {
|
|
204
|
+
query: string;
|
|
205
|
+
target?: 'class' | 'method' | 'property' | 'parameter';
|
|
206
|
+
}): Promise<any>;
|
|
201
207
|
}
|
package/dist/stellify-client.js
CHANGED
|
@@ -243,4 +243,9 @@ export class StellifyClient {
|
|
|
243
243
|
const response = await this.client.get(`/file/${uuid}/source`);
|
|
244
244
|
return response.data;
|
|
245
245
|
}
|
|
246
|
+
// Search PHP 8 attributes - returns suggestions based on query and target
|
|
247
|
+
async searchAttributes(params) {
|
|
248
|
+
const response = await this.client.get('/attributes/search', { params });
|
|
249
|
+
return response.data;
|
|
250
|
+
}
|
|
246
251
|
}
|
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.39",
|
|
10
10
|
"packages": [
|
|
11
11
|
{
|
|
12
12
|
"registryType": "npm",
|
|
13
13
|
"identifier": "@stellisoft/stellify-mcp",
|
|
14
|
-
"version": "0.1.
|
|
14
|
+
"version": "0.1.39",
|
|
15
15
|
"transport": {
|
|
16
16
|
"type": "stdio"
|
|
17
17
|
},
|
|
@@ -1,264 +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
|
-
|
|
29
|
+
> Run: `[scaffold command TBD]`
|
|
30
|
+
>
|
|
31
|
+
> Let me know when this is complete.
|
|
28
32
|
|
|
29
|
-
|
|
33
|
+
Do not proceed until the scaffold is in place.
|
|
30
34
|
|
|
31
|
-
|
|
35
|
+
## Step 2 — Analyse the WordPress Project
|
|
32
36
|
|
|
33
|
-
|
|
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)
|
|
37
|
+
Perform a comprehensive analysis of the WordPress site to understand what's custom. This produces a report before any code generation happens.
|
|
39
38
|
|
|
40
|
-
|
|
39
|
+
### 2a. Identify the Active Theme
|
|
41
40
|
|
|
42
|
-
|
|
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.
|
|
43
42
|
|
|
44
|
-
|
|
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
|
+
```
|
|
45
134
|
|
|
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
|
|
135
|
+
### 2e. Produce Analysis Report
|
|
56
136
|
|
|
57
|
-
|
|
137
|
+
Present a structured summary before proceeding:
|
|
58
138
|
|
|
59
|
-
|
|
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
|
+
```
|
|
60
193
|
|
|
61
|
-
|
|
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
|
|
194
|
+
After presenting the report, ask:
|
|
66
195
|
|
|
67
|
-
|
|
196
|
+
> Does this analysis look correct? Ready to proceed with generating the custom resources?
|
|
68
197
|
|
|
69
|
-
|
|
198
|
+
## Step 3 — Generate Custom Resources
|
|
70
199
|
|
|
71
|
-
|
|
200
|
+
For each custom post type and taxonomy identified, create the necessary Laravel resources using Stellify MCP tools.
|
|
72
201
|
|
|
73
|
-
|
|
202
|
+
### 3a. Custom Post Type Models
|
|
74
203
|
|
|
75
|
-
|
|
204
|
+
For each custom post type, use `create_resources` with `api: false`:
|
|
76
205
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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
|
|
83
212
|
|
|
84
|
-
|
|
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
|
|
85
217
|
|
|
86
|
-
**
|
|
218
|
+
**Controller requirements:**
|
|
219
|
+
- `index()` returning paginated published items
|
|
220
|
+
- `show($model)` returning single item
|
|
221
|
+
- Return arrays (Stellify convention), not views or JSON
|
|
87
222
|
|
|
88
|
-
|
|
223
|
+
Example model structure:
|
|
89
224
|
|
|
90
225
|
```php
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
protected $
|
|
94
|
-
|
|
226
|
+
class Project extends Model
|
|
227
|
+
{
|
|
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
|
+
];
|
|
95
237
|
|
|
96
|
-
// Enable slug-based route model binding (WordPress uses post_name for slugs)
|
|
97
238
|
public function getRouteKeyName(): string
|
|
98
239
|
{
|
|
99
|
-
return '
|
|
240
|
+
return 'slug';
|
|
100
241
|
}
|
|
101
242
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
return $
|
|
105
|
-
->where('post_type', 'post');
|
|
243
|
+
public function author(): BelongsTo
|
|
244
|
+
{
|
|
245
|
+
return $this->belongsTo(User::class, 'author_id');
|
|
106
246
|
}
|
|
107
|
-
}
|
|
108
|
-
```
|
|
109
|
-
|
|
110
|
-
Key WordPress table mappings:
|
|
111
|
-
- Posts & Pages → `wp_posts` (distinguished by `post_type` column: 'post', 'page', or custom types)
|
|
112
|
-
- Categories & Tags → `wp_terms` + `wp_term_taxonomy` + `wp_term_relationships`
|
|
113
|
-
- Users → `wp_users`
|
|
114
|
-
- Post meta / custom fields → `wp_postmeta`
|
|
115
|
-
- Comments → `wp_comments`
|
|
116
|
-
- Navigation menus → `wp_posts` with `post_type = 'nav_menu_item'` + `wp_terms` with taxonomy `nav_menu`
|
|
117
|
-
|
|
118
|
-
In Blade templates, use WordPress column names:
|
|
119
|
-
- `$post->post_title` (not `$post->title`)
|
|
120
|
-
- `$post->post_content` (not `$post->content`)
|
|
121
|
-
- `$post->post_excerpt` (not `$post->excerpt`)
|
|
122
|
-
- `$post->post_name` (this is the slug)
|
|
123
|
-
- `$post->post_date` (not `$post->published_at`)
|
|
124
|
-
- `$post->post_status` (not `$post->status`)
|
|
125
|
-
|
|
126
|
-
**If Mode B (fresh database):**
|
|
127
|
-
|
|
128
|
-
Create new resources with clean Laravel migrations for each model identified in Step 4. Use clean column names (`title`, `slug`, `content`, `excerpt`, `status`, `published_at`, etc.). After the import is complete, suggest a data migration SQL query or artisan command that maps data from the WordPress tables to the new schema.
|
|
129
|
-
|
|
130
|
-
For each model with a `slug` field, add `getRouteKeyName()`:
|
|
131
|
-
|
|
132
|
-
```php
|
|
133
|
-
public function getRouteKeyName(): string
|
|
134
|
-
{
|
|
135
|
-
return 'slug';
|
|
136
|
-
}
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
### 5b. Refine Controller Methods
|
|
140
247
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
public function index(): array
|
|
146
|
-
{
|
|
147
|
-
return ['posts' => Post::where('status', 'published')->latest('published_at')->paginate(10)];
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
public function show(Post $post): array
|
|
151
|
-
{
|
|
152
|
-
return ['post' => $post];
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// WRONG — do not return JSON
|
|
156
|
-
public function index()
|
|
157
|
-
{
|
|
158
|
-
return Post::where('status', 'published')->get();
|
|
159
|
-
}
|
|
248
|
+
public function projectTypes(): BelongsToMany
|
|
249
|
+
{
|
|
250
|
+
return $this->belongsToMany(ProjectType::class);
|
|
251
|
+
}
|
|
160
252
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
253
|
+
public function scopePublished($query)
|
|
254
|
+
{
|
|
255
|
+
return $query->where('status', 'published');
|
|
256
|
+
}
|
|
165
257
|
}
|
|
166
258
|
```
|
|
167
259
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
### 5c. Create Routes
|
|
171
|
-
Create route entries mapping URLs to controller methods.
|
|
172
|
-
|
|
173
|
-
### 5d. Create the Layout (layouts/app.blade.php)
|
|
174
|
-
Read header.php and footer.php. Create a single Blade layout that combines:
|
|
175
|
-
- The HTML structure from header.php (doctype, head, opening body, nav)
|
|
176
|
-
- `@yield('content')` for page content
|
|
177
|
-
- The structure from footer.php (closing elements, footer content)
|
|
178
|
-
|
|
179
|
-
Translate WordPress functions to Blade:
|
|
180
|
-
- `wp_head()` → `@vite(['resources/css/app.css', 'resources/js/app.js'])` and `<meta>` tags
|
|
181
|
-
- `wp_footer()` → nothing needed (Vite handles it)
|
|
182
|
-
- `wp_nav_menu()` → `@include('partials.nav')`
|
|
183
|
-
- `bloginfo('name')` → `{{ config('app.name') }}`
|
|
184
|
-
- `bloginfo('description')` → `{{ config('app.description', '') }}`
|
|
185
|
-
- `body_class()` → appropriate Tailwind classes
|
|
186
|
-
- `language_attributes()` → `lang="{{ str_replace('_', '-', app()->getLocale()) }}"`
|
|
187
|
-
|
|
188
|
-
### 5e. Create Blade Views
|
|
189
|
-
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')`.**
|
|
190
|
-
|
|
191
|
-
**⚠️ 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.
|
|
260
|
+
### 3b. Custom Taxonomy Models
|
|
192
261
|
|
|
193
|
-
|
|
262
|
+
For each custom taxonomy:
|
|
194
263
|
|
|
195
|
-
|
|
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
|
|
196
269
|
|
|
197
|
-
**
|
|
270
|
+
**Migration requirements:**
|
|
271
|
+
- Main taxonomy table
|
|
272
|
+
- Pivot table linking to each associated post type (e.g., `project_project_type`)
|
|
198
273
|
|
|
199
|
-
|
|
200
|
-
|-----------|----------------|----------------|-------------------|
|
|
201
|
-
| `the_title()` | `{{ $item->post_title }}` | `{{ $item->title }}` | `textField: "title"` |
|
|
202
|
-
| `the_content()` | `{!! $item->post_content !!}` | `{!! $item->content !!}` | statement with code |
|
|
203
|
-
| `the_excerpt()` | `{{ $item->post_excerpt }}` | `{{ $item->excerpt }}` | `textField: "excerpt"` |
|
|
204
|
-
| `the_permalink()` | `{{ route('posts.show', $item->post_name) }}` | `{{ route('posts.show', $item->slug) }}` | `hrefExpression: "..."` |
|
|
205
|
-
| `the_date()` | `{{ $item->post_date->format('d M Y') }}` | `{{ $item->published_at->format('d M Y') }}` | statement with code |
|
|
206
|
-
| `the_author()` | `{{ $item->author->display_name }}` | `{{ $item->author->name }}` | statement with code |
|
|
207
|
-
| `get_template_part()` | `@include('partials.post-card', ['post' => $item])` | same | s-directive |
|
|
208
|
-
| `have_posts()` | `@foreach($posts as $item)` | same | s-directive pair |
|
|
274
|
+
### 3c. Routes
|
|
209
275
|
|
|
210
|
-
|
|
276
|
+
Create routes following WordPress URL conventions where sensible:
|
|
211
277
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
- `<!-- wp:post-excerpt -->` → `@if($item->post_excerpt)` ... `@endif`
|
|
217
|
-
- `<!-- wp:post-comments -->` → `@if($item->comments->count() > 0)` ... `@endif`
|
|
218
|
-
- `<!-- wp:query-no-results -->` → `@if($posts->isEmpty())` ... `@endif` (outside loop)
|
|
219
|
-
|
|
220
|
-
**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');
|
|
221
282
|
|
|
222
|
-
|
|
283
|
+
// Custom taxonomy archives
|
|
284
|
+
Route::get('/project-type/{projectType}', [ProjectTypeController::class, 'show'])->name('project-types.show');
|
|
285
|
+
```
|
|
223
286
|
|
|
224
|
-
|
|
225
|
-
1. Do NOT pass raw Blade syntax to `html_to_elements` — it will be stored literally
|
|
226
|
-
2. Create clean HTML first, then use `update_element` to add dynamic attributes:
|
|
227
|
-
- `textField: "title"` → outputs `{{ $item->title }}`
|
|
228
|
-
- `hrefField: "slug"` → outputs `href="{{ $item->slug }}"`
|
|
229
|
-
- `srcField: "featured_image"` → outputs `src="{{ $item->featured_image }}"`
|
|
230
|
-
3. For complex expressions (route helpers, method calls), use:
|
|
231
|
-
- `hrefExpression: "{{ route('posts.show', $item->slug) }}"`
|
|
232
|
-
- `srcExpression: "{{ $item->featured_image }}"`
|
|
233
|
-
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
|
|
234
288
|
|
|
235
|
-
|
|
236
|
-
```
|
|
237
|
-
1. s-directive with statement: "@foreach($posts as $item)"
|
|
238
|
-
2. article element (content to repeat)
|
|
239
|
-
3. s-directive with statement: "@endforeach"
|
|
240
|
-
```
|
|
289
|
+
Create a summary of items requiring manual attention:
|
|
241
290
|
|
|
242
|
-
###
|
|
243
|
-
|
|
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.
|
|
244
293
|
|
|
245
|
-
###
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
- 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
|
|
250
298
|
|
|
251
|
-
|
|
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.
|
|
252
301
|
|
|
253
|
-
|
|
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
|
|
254
309
|
|
|
255
310
|
## Important Rules
|
|
256
311
|
|
|
257
|
-
- **
|
|
258
|
-
- **
|
|
259
|
-
- **
|
|
260
|
-
- **
|
|
261
|
-
- **
|
|
262
|
-
- **
|
|
263
|
-
- **Be pragmatic** — focus on the core templates that define the site's main pages. Skip hyper-specific template variations unless they're clearly important.
|
|
264
|
-
- **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
|