fastmode-mcp 1.0.0

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.
Files changed (67) hide show
  1. package/README.md +561 -0
  2. package/bin/run.js +50 -0
  3. package/dist/index.d.ts +3 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +802 -0
  6. package/dist/lib/api-client.d.ts +81 -0
  7. package/dist/lib/api-client.d.ts.map +1 -0
  8. package/dist/lib/api-client.js +237 -0
  9. package/dist/lib/auth-state.d.ts +13 -0
  10. package/dist/lib/auth-state.d.ts.map +1 -0
  11. package/dist/lib/auth-state.js +24 -0
  12. package/dist/lib/context-fetcher.d.ts +67 -0
  13. package/dist/lib/context-fetcher.d.ts.map +1 -0
  14. package/dist/lib/context-fetcher.js +190 -0
  15. package/dist/lib/credentials.d.ts +52 -0
  16. package/dist/lib/credentials.d.ts.map +1 -0
  17. package/dist/lib/credentials.js +196 -0
  18. package/dist/lib/device-flow.d.ts +14 -0
  19. package/dist/lib/device-flow.d.ts.map +1 -0
  20. package/dist/lib/device-flow.js +244 -0
  21. package/dist/tools/cms-items.d.ts +56 -0
  22. package/dist/tools/cms-items.d.ts.map +1 -0
  23. package/dist/tools/cms-items.js +376 -0
  24. package/dist/tools/create-site.d.ts +9 -0
  25. package/dist/tools/create-site.d.ts.map +1 -0
  26. package/dist/tools/create-site.js +202 -0
  27. package/dist/tools/deploy-package.d.ts +9 -0
  28. package/dist/tools/deploy-package.d.ts.map +1 -0
  29. package/dist/tools/deploy-package.js +434 -0
  30. package/dist/tools/generate-samples.d.ts +19 -0
  31. package/dist/tools/generate-samples.d.ts.map +1 -0
  32. package/dist/tools/generate-samples.js +272 -0
  33. package/dist/tools/get-conversion-guide.d.ts +7 -0
  34. package/dist/tools/get-conversion-guide.d.ts.map +1 -0
  35. package/dist/tools/get-conversion-guide.js +1323 -0
  36. package/dist/tools/get-example.d.ts +7 -0
  37. package/dist/tools/get-example.d.ts.map +1 -0
  38. package/dist/tools/get-example.js +1568 -0
  39. package/dist/tools/get-field-types.d.ts +30 -0
  40. package/dist/tools/get-field-types.d.ts.map +1 -0
  41. package/dist/tools/get-field-types.js +154 -0
  42. package/dist/tools/get-schema.d.ts +5 -0
  43. package/dist/tools/get-schema.d.ts.map +1 -0
  44. package/dist/tools/get-schema.js +320 -0
  45. package/dist/tools/get-started.d.ts +21 -0
  46. package/dist/tools/get-started.d.ts.map +1 -0
  47. package/dist/tools/get-started.js +624 -0
  48. package/dist/tools/get-tenant-schema.d.ts +18 -0
  49. package/dist/tools/get-tenant-schema.d.ts.map +1 -0
  50. package/dist/tools/get-tenant-schema.js +158 -0
  51. package/dist/tools/list-projects.d.ts +5 -0
  52. package/dist/tools/list-projects.d.ts.map +1 -0
  53. package/dist/tools/list-projects.js +101 -0
  54. package/dist/tools/sync-schema.d.ts +41 -0
  55. package/dist/tools/sync-schema.d.ts.map +1 -0
  56. package/dist/tools/sync-schema.js +483 -0
  57. package/dist/tools/validate-manifest.d.ts +5 -0
  58. package/dist/tools/validate-manifest.d.ts.map +1 -0
  59. package/dist/tools/validate-manifest.js +311 -0
  60. package/dist/tools/validate-package.d.ts +5 -0
  61. package/dist/tools/validate-package.d.ts.map +1 -0
  62. package/dist/tools/validate-package.js +337 -0
  63. package/dist/tools/validate-template.d.ts +12 -0
  64. package/dist/tools/validate-template.d.ts.map +1 -0
  65. package/dist/tools/validate-template.js +790 -0
  66. package/package.json +54 -0
  67. package/scripts/postinstall.js +129 -0
@@ -0,0 +1,1323 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getConversionGuide = getConversionGuide;
4
+ const SECTIONS = {
5
+ first_steps: `# REQUIRED FIRST STEPS
6
+
7
+ ## STOP. DO NOT BUILD ANYTHING YET.
8
+
9
+ Before writing ANY HTML, templates, or manifest.json, you MUST:
10
+
11
+ 1. **Determine which project this is for** - Call \`list_projects\`
12
+ 2. **Ask the user** - "Which project?" or "What should I name your new project?"
13
+ 3. **Get a projectId** - Either from existing project or from \`create_site\`
14
+ 4. **Get the schema** - Call \`get_tenant_schema\` to know what fields exist
15
+
16
+ **Skipping these steps WILL cause deployment failures.**
17
+
18
+ ---
19
+
20
+ ## Step 1: Check for Existing Projects
21
+
22
+ Call \`list_projects\` to see the user's existing Fast Mode projects.
23
+
24
+ ## Step 2: Ask the User
25
+
26
+ Based on what \`list_projects\` returns:
27
+
28
+ **If NO projects exist:**
29
+ - This is a new user - ask: "What would you like to name your new project?"
30
+ - Proceed to Step 3b (New Project)
31
+
32
+ **If projects exist:**
33
+ - Present the list and ask: "Is this website for one of your existing projects, or should I create a new one?"
34
+ - Let the user choose which project, or confirm they want a new one
35
+
36
+ ## Step 3a: For EXISTING Projects
37
+
38
+ 1. User selects the project from the list
39
+ 2. Call \`get_tenant_schema(projectId)\` to get the current collections and fields
40
+ 3. **Use this schema to build templates with the correct field names**
41
+ 4. When deploying, this will UPDATE the existing project
42
+ 5. Proceed to Phase 1 (Website Analysis)
43
+
44
+ ## Step 3b: For NEW Projects
45
+
46
+ 1. Ask for the project name if you don't have it
47
+ 2. Call \`create_site(name)\` to create the project
48
+ 3. You'll receive a projectId to use for deployment
49
+ 4. After building the package, call \`sync_schema\` to create collections/fields
50
+ 5. **Ask the user:** "Would you like me to generate sample items so you can preview how the collections will look?"
51
+ 6. If yes, call \`generate_sample_items(projectId)\` - it handles dependency ordering automatically
52
+ 7. Proceed to Phase 1 (Website Analysis)
53
+
54
+ ---
55
+
56
+ **WHY THIS MATTERS:**
57
+ - For existing projects: The schema determines which fields to use in templates
58
+ - For new projects: You need the projectId before you can deploy
59
+ - Always: The user must confirm which project to use - never assume
60
+
61
+ ---
62
+
63
+ ## CHECKPOINT: Before You Continue
64
+
65
+ **STOP.** Confirm you have completed these steps:
66
+
67
+ | Requirement | How to Get It |
68
+ |-------------|---------------|
69
+ | projectId | From \`list_projects\` (existing) or \`create_site\` (new) |
70
+ | Project name confirmed | Asked the user |
71
+ | Schema loaded | Called \`get_tenant_schema(projectId)\` |
72
+
73
+ **If you don't have a projectId, DO NOT PROCEED.** Go back to Step 1.
74
+
75
+ The projectId is REQUIRED for:
76
+ - \`get_tenant_schema\` - to get existing field names
77
+ - \`deploy_package\` - to upload the site
78
+ - \`sync_schema\` - to create new collections
79
+
80
+ `,
81
+ analysis: `# Phase 1: Website Analysis (MANDATORY)
82
+
83
+ Before writing ANY code, complete this analysis:
84
+
85
+ ## 1.1 Map ALL URLs
86
+ Visit every page and document the exact URL structure:
87
+ \`\`\`
88
+ / (Homepage)
89
+ /about or /about-us or /company
90
+ /services or /what-we-do
91
+ /contact or /get-in-touch
92
+ /blog or /news or /resources or /insights
93
+ /blog/post-slug
94
+ /team or /our-team
95
+ \`\`\`
96
+
97
+ ## 1.2 Categorize Each Page
98
+
99
+ **Page Types:**
100
+ - **Static** - Fixed content (/, /about, /contact)
101
+ - **List** - Shows multiple items (/blog, /team)
102
+ - **Detail** - Single item from a list (/blog/my-post)
103
+
104
+ ## 1.3 Identify Content Types
105
+
106
+ Identify repeating content that should be CMS collections:
107
+
108
+ - Time-stamped articles → **Blog/Posts collection**
109
+ - Staff/employee profiles → **Team collection**
110
+ - Downloadable files → **Downloads collection**
111
+ - Product listings → **Products collection**
112
+ - Testimonials → **Testimonials collection**
113
+
114
+ **Note:** Create custom collections in the CMS dashboard. You can use collection templates (Blog, Team, Downloads, etc.) to quickly set up common patterns.
115
+
116
+ ## 1.4 Document Assets
117
+
118
+ List all asset locations:
119
+ \`\`\`
120
+ CSS: /css/*.css, /styles/*.css
121
+ JS: /js/*.js, /scripts/*.js
122
+ Images: /images/*, /assets/img/*
123
+ Fonts: Google Fonts links, /fonts/*
124
+ \`\`\`
125
+
126
+ ## 1.5 Critical Rule
127
+
128
+ **PRESERVE ORIGINAL URLs**
129
+
130
+ If the site uses \`/resources\` for articles, keep \`/resources\`.
131
+ Do NOT change it to \`/blog\`.
132
+
133
+ Use the manifest's path configuration to maintain original URLs.`,
134
+ structure: `# Package Structure
135
+
136
+ Create a package with this structure (can be at repo root OR in a subfolder like cms_package/):
137
+
138
+ \`\`\`
139
+ website-package/ # Can be at root or in a subfolder
140
+ ├── manifest.json # Required: Defines pages and CMS templates
141
+ ├── public/ # Static assets (CSS, JS, images, fonts)
142
+ │ ├── css/
143
+ │ │ └── style.css
144
+ │ ├── js/
145
+ │ │ └── main.js
146
+ │ └── images/
147
+ │ └── logo.png
148
+ ├── pages/ # HTML pages (static content)
149
+ │ ├── index.html
150
+ │ ├── about.html
151
+ │ └── contact.html
152
+ └── templates/ # CMS templates (dynamic content)
153
+ ├── posts_index.html
154
+ ├── posts_detail.html
155
+ ├── team_index.html
156
+ └── services_index.html
157
+ \`\`\`
158
+
159
+ **SUPPORTED REPO STRUCTURES:**
160
+ \`\`\`
161
+ Option 1: CMS files at root
162
+ repo/
163
+ ├── manifest.json
164
+ ├── pages/
165
+ ├── public/
166
+ └── templates/
167
+
168
+ Option 2: CMS files in subfolder (e.g. for Next.js projects)
169
+ repo/
170
+ ├── app/ # Next.js app (ignored)
171
+ ├── cms_package/ # CMS files here
172
+ │ ├── manifest.json
173
+ │ ├── pages/
174
+ │ ├── public/
175
+ │ └── templates/
176
+ └── package.json
177
+ \`\`\`
178
+
179
+ ## Folder Rules
180
+
181
+ ### public/
182
+ - ALL assets go here (CSS, JS, images, fonts)
183
+ - Maintains subfolder structure
184
+ - Referenced as \`/public/...\` in HTML
185
+
186
+ ### pages/
187
+ - Static HTML pages
188
+ - One file per page
189
+ - Use data-edit-key for editable text
190
+
191
+ ### templates/
192
+ - CMS-powered pages
193
+ - Use {{tokens}} for dynamic content
194
+ - Named by collection slug (e.g., posts_index.html, posts_detail.html)`,
195
+ seo: `# SEO Tags (CRITICAL - Do NOT Include)
196
+
197
+ Fast Mode automatically manages all SEO meta tags. Do NOT include these in your HTML templates:
198
+
199
+ ## Tags to EXCLUDE
200
+
201
+ | Tag | Reason |
202
+ |-----|--------|
203
+ | \`<title>...\` | Managed via Settings → SEO & Design |
204
+ | \`<meta name="description">\` | Managed via Settings → SEO & Design |
205
+ | \`<meta name="keywords">\` | Managed via Settings → SEO & Design |
206
+ | \`<meta property="og:*">\` | Open Graph tags auto-generated |
207
+ | \`<meta name="twitter:*">\` | Twitter cards auto-generated |
208
+ | \`<link rel="icon">\` | Favicon managed in settings |
209
+ | \`<link rel="shortcut icon">\` | Favicon managed in settings |
210
+ | \`<link rel="apple-touch-icon">\` | Managed by Fast Mode |
211
+ | \`<meta name="google-site-verification">\` | Managed in settings |
212
+
213
+ ## Correct \`<head>\` Structure
214
+
215
+ \`\`\`html
216
+ <head>
217
+ <meta charset="UTF-8">
218
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
219
+ <!-- SEO managed by Fast Mode -->
220
+ <link rel="stylesheet" href="/public/css/style.css">
221
+ <!-- Font imports, external scripts, etc. are OK -->
222
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap" rel="stylesheet">
223
+ </head>
224
+ \`\`\`
225
+
226
+ ## How Fast Mode SEO Works
227
+
228
+ 1. **Global SEO** (Settings → SEO & Design)
229
+ - Site-wide defaults: title, description, OG image, favicon
230
+ - Applied to all pages
231
+
232
+ 2. **Page-Level SEO** (Editor → click page → SEO tab)
233
+ - Override specific pages with custom title/description
234
+ - Page-level settings take priority over global
235
+
236
+ 3. **Automatic Injection**
237
+ - Fast Mode strips any hardcoded SEO tags from templates
238
+ - Injects correct SEO from settings at render time
239
+ - Guarantees single source of truth
240
+
241
+ ## Why This Matters
242
+
243
+ - No duplicate meta tags (bad for SEO ranking)
244
+ - Content managers can update SEO without touching code
245
+ - Consistent SEO across all pages
246
+ - Works retroactively on existing templates`,
247
+ manifest: `# manifest.json Configuration
248
+
249
+ ## Basic Structure
250
+
251
+ \`\`\`json
252
+ {
253
+ "pages": [
254
+ {
255
+ "path": "/",
256
+ "file": "pages/index.html",
257
+ "title": "Home",
258
+ "editable": true
259
+ }
260
+ ],
261
+ "cmsTemplates": {
262
+ "postsIndex": "templates/posts_index.html",
263
+ "postsIndexPath": "/blog",
264
+ "postsDetail": "templates/posts_detail.html",
265
+ "postsDetailPath": "/blog",
266
+ "teamIndex": "templates/team_index.html",
267
+ "teamIndexPath": "/team"
268
+ },
269
+ "defaultHeadHtml": "<!-- fonts, meta tags -->"
270
+ }
271
+ \`\`\`
272
+
273
+ ## CMS Template Configuration
274
+
275
+ All CMS collections use the **flat format** with four properties per collection:
276
+
277
+ \`\`\`json
278
+ {
279
+ "cmsTemplates": {
280
+ // Collection: posts
281
+ "postsIndex": "templates/posts_index.html",
282
+ "postsIndexPath": "/blog",
283
+ "postsDetail": "templates/posts_detail.html",
284
+ "postsDetailPath": "/blog",
285
+
286
+ // Collection: team (no detail pages)
287
+ "teamIndex": "templates/team_index.html",
288
+ "teamIndexPath": "/team",
289
+
290
+ // Collection: services
291
+ "servicesIndex": "pages/services.html",
292
+ "servicesIndexPath": "/services",
293
+ "servicesDetail": "templates/service-detail.html",
294
+ "servicesDetailPath": "/services"
295
+ }
296
+ }
297
+ \`\`\`
298
+
299
+ **Path Configuration Pattern:**
300
+ - \`{collectionSlug}Index\` - Template file for the index/listing page
301
+ - \`{collectionSlug}IndexPath\` - URL for the collection list page
302
+ - \`{collectionSlug}Detail\` - Template file for the detail page
303
+ - \`{collectionSlug}DetailPath\` - Base URL for detail pages (item slug appended)
304
+
305
+ **IMPORTANT:** The collection slug in the CMS is for the database. URL paths come EXCLUSIVELY from the manifest.
306
+
307
+ ## Page Configuration
308
+
309
+ Each page needs:
310
+ - \`path\` - The URL (must start with /)
311
+ - \`file\` - Path to HTML file in pages/
312
+ - \`title\` - Page title for CMS
313
+ - \`editable\` - Enable inline editing (optional)
314
+
315
+ ## Two Ways to Configure CMS Templates
316
+
317
+ ### Option 1: In manifest.json (during package creation)
318
+ Configure \`cmsTemplates\` section as shown above when building the package.
319
+
320
+ ### Option 2: Via Settings UI (after upload)
321
+ After uploading a package, go to **Dashboard → Settings → CMS Templates** to configure template mappings.`,
322
+ templates: `# Template Creation Guide
323
+
324
+ ## Template Types
325
+
326
+ 1. **Index Templates** - List pages (posts_index.html)
327
+ 2. **Detail Templates** - Single item pages (posts_detail.html)
328
+ 3. **Static Pages** - Fixed content with data-edit-key
329
+
330
+ ## Static UI vs Dynamic Content
331
+
332
+ When converting templates, distinguish between:
333
+
334
+ ### 1. Static UI Elements (Keep as-is)
335
+ Logos, icons, decorative backgrounds - these stay as \`/public/\` paths:
336
+ \`\`\`html
337
+ <!-- KEEP these static -->
338
+ <img src="/public/images/logo.png" alt="Company Logo">
339
+ <img src="/public/images/icons/arrow.svg" alt="">
340
+ \`\`\`
341
+
342
+ ### 2. Dynamic Content (Replace with CMS tokens)
343
+ Blog posts, team members, products - replace example content with \`{{#each}}\` loops:
344
+
345
+ \`\`\`html
346
+ <!-- WRONG: Hardcoded example content -->
347
+ <article class="post-card">
348
+ <img src="/images/example-post.jpg" alt="Example Post">
349
+ <h2>My Example Post Title</h2>
350
+ <p>This is a sample description...</p>
351
+ </article>
352
+
353
+ <!-- CORRECT: CMS tokens with loop -->
354
+ {{#each posts sort="publishedAt" order="desc"}}
355
+ <article class="post-card">
356
+ {{#if image}}
357
+ <img src="{{image}}" alt="{{name}}">
358
+ {{/if}}
359
+ <h2><a href="{{url}}">{{name}}</a></h2>
360
+ <p>{{summary}}</p>
361
+ </article>
362
+ {{/each}}
363
+ \`\`\`
364
+
365
+ **Rule of thumb:** If it's site branding/design → keep static. If it's content that changes per item → use CMS tokens.
366
+
367
+ ## Index Template Pattern
368
+
369
+ \`\`\`html
370
+ <main class="posts-page">
371
+ <h1 data-edit-key="posts-title">Blog</h1>
372
+
373
+ <div class="posts-grid">
374
+ {{#each posts limit=12 sort="publishedAt" order="desc"}}
375
+ <article class="post-card">
376
+ {{#if image}}
377
+ <img src="{{image}}" alt="{{name}}">
378
+ {{/if}}
379
+ <h2><a href="{{url}}">{{name}}</a></h2>
380
+ <p>{{summary}}</p>
381
+ {{#if author}}
382
+ <span>By {{author.name}}</span>
383
+ {{/if}}
384
+ </article>
385
+ {{/each}}
386
+ </div>
387
+
388
+ {{#unless posts}}
389
+ <p>No posts yet. Check back soon!</p>
390
+ {{/unless}}
391
+ </main>
392
+ \`\`\`
393
+
394
+ ## Detail Template Pattern
395
+
396
+ \`\`\`html
397
+ <article class="post-detail">
398
+ {{#if image}}
399
+ <img src="{{image}}" alt="{{name}}">
400
+ {{/if}}
401
+
402
+ <h1>{{name}}</h1>
403
+
404
+ {{#if author}}
405
+ <div class="author">
406
+ By <a href="{{author.url}}">{{author.name}}</a>
407
+ </div>
408
+ {{/if}}
409
+
410
+ {{#if publishedAt}}
411
+ <time>{{publishedAt}}</time>
412
+ {{/if}}
413
+
414
+ <div class="content">
415
+ {{{body}}}
416
+ </div>
417
+ </article>
418
+ \`\`\`
419
+
420
+ ## Team Template Pattern
421
+
422
+ \`\`\`html
423
+ <div class="team-grid">
424
+ {{#each team sort="order" order="asc"}}
425
+ <div class="team-member">
426
+ {{#if photo}}
427
+ <img src="{{photo}}" alt="{{name}}">
428
+ {{/if}}
429
+ <h3>{{name}}</h3>
430
+ {{#if role}}
431
+ <p class="role">{{role}}</p>
432
+ {{/if}}
433
+ {{#if bio}}
434
+ <div class="bio">{{{bio}}}</div>
435
+ {{/if}}
436
+ </div>
437
+ {{/each}}
438
+ </div>
439
+ \`\`\`
440
+
441
+ ## Static Page Pattern
442
+
443
+ \`\`\`html
444
+ <main class="about-page">
445
+ <h1 data-edit-key="about-title">About Us</h1>
446
+ <p data-edit-key="about-intro">Introduction text...</p>
447
+
448
+ <section>
449
+ <h2 data-edit-key="about-mission-title">Our Mission</h2>
450
+ <p data-edit-key="about-mission-text">Mission statement...</p>
451
+ </section>
452
+ </main>
453
+ \`\`\`
454
+
455
+ ## Template Must-Haves
456
+
457
+ - Include complete header (copy from source)
458
+ - Include complete footer (copy from source)
459
+ - All asset paths use /public/
460
+ - Rich text fields use {{{triple braces}}}`,
461
+ tokens: `# Token Reference
462
+
463
+ ## Double Braces {{...}} - Escaped Output
464
+ Use for text, URLs, images:
465
+ \`\`\`html
466
+ {{name}}
467
+ {{image}}
468
+ {{url}}
469
+ {{author.name}}
470
+ \`\`\`
471
+
472
+ ## Triple Braces {{{...}}} - Raw HTML
473
+ Use for rich text content:
474
+ \`\`\`html
475
+ {{{body}}}
476
+ {{{bio}}}
477
+ {{{description}}}
478
+ \`\`\`
479
+
480
+ **CRITICAL:** If a field contains HTML (richText type), you MUST use triple braces!
481
+
482
+ ## Loop Syntax
483
+ \`\`\`html
484
+ {{#each collectionSlug}}
485
+ ...content for each item...
486
+ {{/each}}
487
+ \`\`\`
488
+
489
+ **Modifiers:**
490
+ - \`limit=N\` - Maximum items
491
+ - \`featured=true\` - Only featured (if collection has boolean "featured" field)
492
+ - \`sort="field"\` - Sort by field
493
+ - \`order="asc|desc"\` - Sort direction
494
+ - \`where="field:value"\` - Filter by field value
495
+
496
+ ## Nested Loops (Hierarchical Content)
497
+
498
+ For content hierarchies like categories with items, docs sections with pages, or any parent-child relationships.
499
+
500
+ **Works on ALL page types:** static pages, index pages, AND detail pages.
501
+
502
+ \`\`\`html
503
+ {{#each doc_categories}}
504
+ <div class="category-section">
505
+ <h3>{{name}}</h3>
506
+ <ul>
507
+ {{#each doc_pages where="category.slug:{{slug}}"}}
508
+ <li><a href="{{url}}">{{name}}</a></li>
509
+ {{/each}}
510
+ </ul>
511
+ </div>
512
+ {{/each}}
513
+ \`\`\`
514
+
515
+ **How it works:**
516
+ - The outer loop iterates through categories
517
+ - \`{{slug}}\` in the where clause references the current category's slug
518
+ - The inner loop filters doc_pages to only those matching that category
519
+
520
+ ---
521
+
522
+ ### Using @root. Prefix
523
+
524
+ Use \`@root.\` to access collections from the root context when you need to be explicit:
525
+
526
+ \`\`\`html
527
+ {{#each doc_categories}}
528
+ <div class="category-section">
529
+ <h3>{{name}}</h3>
530
+ <ul>
531
+ {{#each @root.doc_pages where="category.slug:{{slug}}"}}
532
+ <li><a href="{{url}}">{{name}}</a></li>
533
+ {{/each}}
534
+ </ul>
535
+ </div>
536
+ {{/each}}
537
+ \`\`\`
538
+
539
+ **When to use @root.:**
540
+ - When the inner collection name might be ambiguous
541
+ - When you want to be explicit about accessing root-level data
542
+ - Both \`{{#each collection}}\` and \`{{#each @root.collection}}\` work the same
543
+
544
+ ---
545
+
546
+ ### Common Patterns
547
+
548
+ \`\`\`html
549
+ <!-- Categories with posts -->
550
+ {{#each categories}}
551
+ <section>
552
+ <h2>{{name}}</h2>
553
+ {{#each posts where="category.slug:{{slug}}"}}
554
+ <article>{{name}}</article>
555
+ {{/each}}
556
+ </section>
557
+ {{/each}}
558
+
559
+ <!-- Authors with their articles -->
560
+ {{#each authors}}
561
+ <div class="author-section">
562
+ <h3>{{name}}</h3>
563
+ {{#each posts where="author.id:{{id}}"}}
564
+ <a href="{{url}}">{{name}}</a>
565
+ {{/each}}
566
+ </div>
567
+ {{/each}}
568
+
569
+ <!-- Documentation sidebar (on any page type) -->
570
+ {{#each doc_categories sort="order" order="asc"}}
571
+ <div class="sidebar-section">
572
+ <h4>{{name}}</h4>
573
+ {{#each @root.doc_pages where="category.slug:{{slug}}" sort="order" order="asc"}}
574
+ <a href="{{url}}">{{name}}</a>
575
+ {{/each}}
576
+ </div>
577
+ {{/each}}
578
+ \`\`\`
579
+
580
+ **Important:** The \`{{field}}\` in the where clause is resolved from the outer loop's current item.
581
+
582
+ ## Conditionals
583
+ \`\`\`html
584
+ {{#if fieldName}}
585
+ ...show if truthy...
586
+ {{else}}
587
+ ...show if falsy...
588
+ {{/if}}
589
+
590
+ {{#unless fieldName}}
591
+ ...show if falsy...
592
+ {{/unless}}
593
+ \`\`\`
594
+
595
+ ## Equality Comparisons
596
+ \`\`\`html
597
+ <!-- Show when equal -->
598
+ {{#if (eq fieldA fieldB)}}...{{/if}}
599
+
600
+ <!-- Show when NOT equal (common for Related Posts) -->
601
+ {{#unless (eq slug ../slug)}}
602
+ <a href="{{url}}">{{name}}</a>
603
+ {{/unless}}
604
+
605
+ <!-- Compare to literal string -->
606
+ {{#eq status "published"}}...{{/eq}}
607
+ \`\`\`
608
+
609
+ ## Comparison Helpers (Numeric Comparisons)
610
+ \`\`\`html
611
+ <!-- Less than: show first 4 items differently -->
612
+ {{#if (lt @index 4)}}
613
+ <div class="featured">{{name}}</div>
614
+ {{else}}
615
+ <div class="standard">{{name}}</div>
616
+ {{/if}}
617
+
618
+ <!-- Greater than: skip first item -->
619
+ {{#if (gt @index 0)}}
620
+ <article>{{name}}</article>
621
+ {{/if}}
622
+
623
+ <!-- Less than or equal -->
624
+ {{#if (lte @index 4)}}...{{/if}}
625
+
626
+ <!-- Greater than or equal -->
627
+ {{#if (gte @index 2)}}...{{/if}}
628
+
629
+ <!-- Not equal (for strings/numbers) -->
630
+ {{#if (ne status "draft")}}
631
+ <span>Published</span>
632
+ {{/if}}
633
+ \`\`\`
634
+
635
+ **Available comparison helpers:**
636
+ | Helper | Meaning | Example |
637
+ |--------|---------|---------|
638
+ | \`lt\` | Less than | \`{{#if (lt @index 4)}}\` |
639
+ | \`gt\` | Greater than | \`{{#if (gt @index 0)}}\` |
640
+ | \`lte\` | Less than or equal | \`{{#if (lte price 100)}}\` |
641
+ | \`gte\` | Greater than or equal | \`{{#if (gte stock 5)}}\` |
642
+ | \`ne\` | Not equal | \`{{#if (ne status "draft")}}\` |
643
+ | \`eq\` | Equal | \`{{#if (eq category.slug ../slug)}}\` |
644
+
645
+ **Works with:**
646
+ - Loop variables: \`@index\`, \`@length\`
647
+ - Numeric fields: \`price\`, \`stock\`, \`order\`
648
+ - String fields: \`status\`, \`type\`
649
+ - Literal values: \`4\`, \`"published"\`
650
+
651
+ ## Loop Variables
652
+ Inside {{#each}} loops, these special variables are available:
653
+
654
+ **Standalone tokens:**
655
+ - \`{{@first}}\` - true for first item
656
+ - \`{{@last}}\` - true for last item
657
+ - \`{{@index}}\` - zero-based index (0, 1, 2...)
658
+ - \`{{@length}}\` - total number of items
659
+
660
+ **Conditional usage:**
661
+ \`\`\`html
662
+ {{#each posts}}
663
+ {{#if @first}}<div class="featured">{{/if}}
664
+ {{#unless @first}}<hr class="separator">{{/unless}}
665
+ <article>{{name}}</article>
666
+ {{#unless @last}},{{/unless}}
667
+ {{/each}}
668
+ \`\`\`
669
+
670
+ | Pattern | Use Case |
671
+ |---------|----------|
672
+ | \`{{#if @first}}\` | Style first item differently |
673
+ | \`{{#unless @first}}\` | Add separator before all but first |
674
+ | \`{{#if @last}}\` | Style last item differently |
675
+ | \`{{#unless @last}}\` | Add comma/separator after all but last |
676
+
677
+ **Important:** Loop variables only work inside \`{{#each}}\` blocks. Using them outside a loop will not work.
678
+
679
+ ## Parent Context (\`../\`)
680
+ Inside loops, access the parent scope:
681
+ \`\`\`html
682
+ <!-- Filter posts by current author -->
683
+ {{#each posts}}
684
+ {{#if (eq author.name ../name)}}
685
+ <h3>{{name}}</h3>
686
+ {{/if}}
687
+ {{/each}}
688
+
689
+ <!-- Related Posts (exclude current) -->
690
+ {{#each posts limit=3}}
691
+ {{#unless (eq slug ../slug)}}
692
+ <a href="{{url}}">{{name}}</a>
693
+ {{/unless}}
694
+ {{/each}}
695
+ \`\`\`
696
+
697
+ - \`../name\` - Parent item's name field
698
+ - \`../slug\` - Parent item's slug
699
+ - \`../fieldName\` - Any field from parent scope
700
+
701
+ **Use cases:**
702
+ - Author pages: filter posts by current author
703
+ - Category pages: filter items by current category
704
+ - Related items: match based on current page
705
+
706
+ ## Built-in Fields (Available on ALL items)
707
+
708
+ Every collection item automatically has:
709
+ - \`{{name}}\` - Item name/title (REQUIRED)
710
+ - \`{{slug}}\` - Item URL slug
711
+ - \`{{url}}\` - Full URL to detail page
712
+ - \`{{publishedAt}}\` - Publish date
713
+ - \`{{createdAt}}\` - Creation date
714
+ - \`{{updatedAt}}\` - Last modified date
715
+
716
+ ## Custom Fields
717
+
718
+ Custom fields are defined when creating collections. The token matches the field slug:
719
+ \`\`\`html
720
+ {{#each services sort="publishedAt" order="desc"}}
721
+ <h2><a href="{{url}}">{{name}}</a></h2>
722
+ {{{description}}}
723
+ <img src="{{image}}" alt="{{name}}">
724
+ <span class="price">\${{price}}</span>
725
+ {{/each}}
726
+ \`\`\`
727
+
728
+ ## Relation Fields
729
+
730
+ Access related item data with dot notation:
731
+ \`\`\`html
732
+ {{#each posts}}
733
+ {{#if category}}
734
+ <span class="category">{{category.name}}</span>
735
+ {{/if}}
736
+ {{/each}}
737
+ \`\`\`
738
+
739
+ **Use \`get_tenant_schema\` tool to see exact fields for a specific project.**`,
740
+ forms: `# Form Handling
741
+
742
+ Forms are automatically captured by the CMS.
743
+
744
+ ## Basic Form Setup
745
+
746
+ \`\`\`html
747
+ <form data-form-name="contact" action="/_forms/contact" method="POST">
748
+ <input type="text" name="name" required>
749
+ <input type="email" name="email" required>
750
+ <textarea name="message" required></textarea>
751
+ <button type="submit">Send</button>
752
+ </form>
753
+ \`\`\`
754
+
755
+ ## Required Attributes
756
+
757
+ - \`data-form-name="xxx"\` on the form element
758
+ - \`action="/_forms/xxx"\` pointing to the form endpoint (MUST match data-form-name)
759
+ - \`method="POST"\` for form submission
760
+ - \`name="fieldName"\` on each input
761
+
762
+ ## Form Handler Script
763
+
764
+ Add to your JavaScript (typically /public/js/main.js):
765
+
766
+ \`\`\`javascript
767
+ document.querySelectorAll('form[data-form-name]').forEach(form => {
768
+ form.addEventListener('submit', async (e) => {
769
+ e.preventDefault();
770
+
771
+ const formName = form.dataset.formName || 'general';
772
+ const formData = new FormData(form);
773
+ const data = Object.fromEntries(formData);
774
+
775
+ // IMPORTANT: Endpoint is /_forms/{formName}
776
+ const response = await fetch('/_forms/' + formName, {
777
+ method: 'POST',
778
+ headers: { 'Content-Type': 'application/json' },
779
+ body: JSON.stringify(data)
780
+ });
781
+
782
+ if (response.ok) {
783
+ form.reset();
784
+ alert(form.dataset.successMessage || 'Thank you!');
785
+ }
786
+ });
787
+ });
788
+ \`\`\`
789
+
790
+ **CRITICAL:** The form endpoint is \`/_forms/{formName}\` - NOT \`/api/forms/submit\`
791
+
792
+ ---
793
+
794
+ ## CRITICAL: Remove Original Form Handlers
795
+
796
+ **If the source site has JavaScript that handles form submissions, you MUST modify or remove it!**
797
+
798
+ The original site's JavaScript often does:
799
+ - \`e.preventDefault()\` - blocks the actual form submission
800
+ - Shows a "fake" success toast/message - but data goes nowhere
801
+ - Never actually submits to a server
802
+
803
+ ### What to Look For in Original JS
804
+
805
+ \`\`\`javascript
806
+ // PROBLEM: This blocks real submissions!
807
+ form.addEventListener('submit', (e) => {
808
+ e.preventDefault();
809
+ // ... validation ...
810
+ showToast('Message sent!'); // FAKE! Data not saved!
811
+ });
812
+ \`\`\`
813
+
814
+ ### What You Must Do
815
+
816
+ **Option A: Use Native Form Submission (Simplest)**
817
+ \`\`\`html
818
+ <form data-form-name="contact" action="/_forms/contact" method="POST">
819
+ <input type="text" name="name" required>
820
+ <input type="email" name="email" required>
821
+ <button type="submit">Send</button>
822
+ </form>
823
+ \`\`\`
824
+ Then **remove** the original JavaScript form handler entirely.
825
+
826
+ **Option B: Keep JS but POST to Fast Mode**
827
+ Replace the original handler with one that actually submits:
828
+ \`\`\`javascript
829
+ form.addEventListener('submit', async (e) => {
830
+ e.preventDefault();
831
+ const formName = form.dataset.formName;
832
+ const response = await fetch('/_forms/' + formName, {
833
+ method: 'POST',
834
+ headers: { 'Content-Type': 'application/json' },
835
+ body: JSON.stringify(Object.fromEntries(new FormData(form)))
836
+ });
837
+ if (response.ok) {
838
+ showToast('Message sent!'); // NOW it's real!
839
+ form.reset();
840
+ }
841
+ });
842
+ \`\`\`
843
+
844
+ ---
845
+
846
+ ## Naming Conventions
847
+
848
+ - Contact form → \`contact\`
849
+ - Newsletter → \`newsletter\`
850
+ - Quote request → \`quote-request\`
851
+ - Application → \`job-application\`
852
+
853
+ ## Important
854
+
855
+ **Tenant context is automatic** - the system handles associating forms with the correct site.`,
856
+ assets: `# Asset Path Rules
857
+
858
+ ## Static Assets (CSS, JS, Fonts, Site Images)
859
+
860
+ **ALL static asset paths must use /public/ prefix**
861
+
862
+ ### HTML Examples
863
+
864
+ \`\`\`html
865
+ <!-- CSS -->
866
+ <link rel="stylesheet" href="/public/css/style.css">
867
+
868
+ <!-- JavaScript -->
869
+ <script src="/public/js/main.js"></script>
870
+
871
+ <!-- Static Images (logos, icons, decorative) -->
872
+ <img src="/public/images/logo.png" alt="Logo">
873
+
874
+ <!-- Favicon -->
875
+ <link rel="icon" href="/public/images/favicon.ico">
876
+ \`\`\`
877
+
878
+ ### CSS Examples
879
+
880
+ \`\`\`css
881
+ /* Correct */
882
+ background-image: url('/public/images/hero.jpg');
883
+ src: url('/public/fonts/custom.woff2');
884
+
885
+ /* Wrong */
886
+ background-image: url('../images/hero.jpg');
887
+ background-image: url('images/hero.jpg');
888
+ \`\`\`
889
+
890
+ ### Conversion Table
891
+
892
+ | Original | Converted |
893
+ |----------|-----------|
894
+ | css/style.css | /public/css/style.css |
895
+ | ../css/style.css | /public/css/style.css |
896
+ | ./images/logo.png | /public/images/logo.png |
897
+ | /images/logo.png | /public/images/logo.png |
898
+
899
+ ---
900
+
901
+ ## CMS Content Images (CRITICAL)
902
+
903
+ **CMS-managed images are different from static assets. They are uploaded by users through the CMS dashboard and served from a CDN.**
904
+
905
+ ### Two Types of Images
906
+
907
+ 1. **Static assets** (\`/public/images/\`) - Site logos, icons, decorative elements bundled in the package
908
+ 2. **CMS images** (\`{{fieldName}}\`) - User-uploaded content images managed through the dashboard
909
+
910
+ ### CMS Image Handling
911
+
912
+ **ALWAYS use CMS tokens for content images - NEVER hardcode URLs**
913
+
914
+ \`\`\`html
915
+ <!-- CORRECT: CMS token for dynamic content -->
916
+ {{#if image}}
917
+ <img src="{{image}}" alt="{{name}}">
918
+ {{/if}}
919
+
920
+ <!-- WRONG: Hardcoded example content -->
921
+ <img src="/images/example-post.jpg" alt="Example Post">
922
+ \`\`\`
923
+
924
+ ### Image Field Examples
925
+
926
+ \`\`\`html
927
+ <!-- Post images -->
928
+ <img src="{{image}}" alt="{{name}}">
929
+ <img src="{{thumbnail}}" alt="{{name}}">
930
+
931
+ <!-- Team member photos -->
932
+ <img src="{{photo}}" alt="{{name}}">
933
+
934
+ <!-- Custom collection images (field slug becomes token) -->
935
+ <img src="{{heroImage}}" alt="{{name}}">
936
+ <img src="{{productImage}}" alt="{{name}}">
937
+ \`\`\`
938
+
939
+ ### Common Mistake: Mixing Static and CMS Images
940
+
941
+ \`\`\`html
942
+ <!-- WRONG: Don't mix hardcoded images with CMS content -->
943
+ {{#each products}}
944
+ <img src="/images/product-placeholder.jpg" alt="Product"> <!-- BAD -->
945
+ <h2>{{name}}</h2>
946
+ {{/each}}
947
+
948
+ <!-- CORRECT: All content comes from CMS -->
949
+ {{#each products}}
950
+ {{#if image}}
951
+ <img src="{{image}}" alt="{{name}}">
952
+ {{/if}}
953
+ <h2>{{name}}</h2>
954
+ {{/each}}
955
+ \`\`\`
956
+
957
+ ---
958
+
959
+ ## External Assets
960
+
961
+ Keep external URLs unchanged:
962
+ \`\`\`html
963
+ <!-- These stay the same -->
964
+ <link href="https://fonts.googleapis.com/..." rel="stylesheet">
965
+ <script src="https://cdn.example.com/lib.js"></script>
966
+ \`\`\``,
967
+ checklist: `# Pre-Submission Checklist
968
+
969
+ ## Structure
970
+ - [ ] manifest.json at package root
971
+ - [ ] Static pages in pages/ folder
972
+ - [ ] Assets in public/ folder
973
+ - [ ] Templates in templates/ (or pages/)
974
+
975
+ ## SEO (CRITICAL)
976
+ - [ ] NO \`<title>\` tags in HTML
977
+ - [ ] NO \`<meta name="description">\` tags
978
+ - [ ] NO \`<meta property="og:*">\` tags
979
+ - [ ] NO \`<meta name="twitter:*">\` tags
980
+ - [ ] NO \`<link rel="icon">\` tags
981
+ - [ ] Include \`<!-- SEO managed by Fast Mode -->\` comment in head
982
+
983
+ ## manifest.json
984
+ - [ ] All static pages listed in pages array
985
+ - [ ] Each page has path, file, title
986
+ - [ ] CMS templates configured with {slug}Index, {slug}IndexPath, {slug}Detail, {slug}DetailPath
987
+ - [ ] Paths match original site URLs
988
+
989
+ ## Assets
990
+ - [ ] ALL CSS uses /public/ paths
991
+ - [ ] ALL JS uses /public/ paths
992
+ - [ ] ALL images use /public/ paths
993
+ - [ ] CSS background-image urls updated
994
+ - [ ] Font paths in CSS updated
995
+
996
+ ## Templates
997
+ - [ ] Templates include header/footer
998
+ - [ ] {{#each}} loops have {{/each}}
999
+ - [ ] {{#if}} conditions have {{/if}}
1000
+ - [ ] {{#eq}} comparisons have {{/eq}}
1001
+ - [ ] Rich text uses {{{triple braces}}}
1002
+ - [ ] Parent refs (../) only inside loops
1003
+ - [ ] Correct field names used
1004
+ - [ ] **Static UI images** (logos, icons) use \`/public/\` paths
1005
+ - [ ] **Content images** use CMS tokens
1006
+ - [ ] **No hardcoded example content** - dynamic items use \`{{#each}}\` loops
1007
+ - [ ] Index pages iterate over collections, not static example cards
1008
+
1009
+ ## Static Pages
1010
+ - [ ] data-edit-key on editable text
1011
+ - [ ] Keys are unique and descriptive
1012
+ - [ ] Forms have data-form-name
1013
+
1014
+ ## Final Validation
1015
+ - [ ] Run validate_manifest with manifest.json
1016
+ - [ ] Run validate_template on each template
1017
+ - [ ] Run validate_package for full check`,
1018
+ common_mistakes: `# COMMON MISTAKES TO AVOID
1019
+
1020
+ **IMPORTANT:** AI agents frequently make these mistakes during website conversion. Read this section carefully before building a package.
1021
+
1022
+ ---
1023
+
1024
+ ## 1. WRONG Manifest Format
1025
+
1026
+ AIs often use a nested object format that Fast Mode does NOT support.
1027
+
1028
+ **WRONG (will NOT work):**
1029
+ \`\`\`json
1030
+ {
1031
+ "collections": {
1032
+ "posts": {
1033
+ "indexPath": "/blog",
1034
+ "indexFile": "collections/posts/index.html",
1035
+ "detailPath": "/blog/:slug",
1036
+ "detailFile": "collections/posts/detail.html"
1037
+ }
1038
+ }
1039
+ }
1040
+ \`\`\`
1041
+
1042
+ **CORRECT (use this):**
1043
+ \`\`\`json
1044
+ {
1045
+ "cmsTemplates": {
1046
+ "postsIndex": "templates/posts_index.html",
1047
+ "postsDetail": "templates/posts_detail.html",
1048
+ "postsIndexPath": "/blog",
1049
+ "postsDetailPath": "/blog"
1050
+ }
1051
+ }
1052
+ \`\`\`
1053
+
1054
+ Key differences:
1055
+ - Use \`cmsTemplates\`, NOT \`collections\`
1056
+ - Use FLAT keys: \`{slug}Index\`, \`{slug}Detail\`, \`{slug}IndexPath\`, \`{slug}DetailPath\`
1057
+ - DO NOT nest objects inside collection names
1058
+
1059
+ ---
1060
+
1061
+ ## 2. WRONG Asset Folder
1062
+
1063
+ Fast Mode serves static assets from \`/public/\`, NOT \`/assets/\`.
1064
+
1065
+ **WRONG:**
1066
+ \`\`\`
1067
+ assets/
1068
+ ├── css/style.css ← Will return 404!
1069
+ └── js/main.js ← Will return 404!
1070
+ \`\`\`
1071
+
1072
+ **CORRECT:**
1073
+ \`\`\`
1074
+ public/
1075
+ ├── css/style.css ← Reference as /public/css/style.css in HTML
1076
+ └── js/main.js ← Reference as /public/js/main.js in HTML
1077
+ \`\`\`
1078
+
1079
+ If CSS/JS isn't loading, check that:
1080
+ - Files are in \`public/\` folder (NOT \`assets/\`)
1081
+ - HTML references use \`/public/css/style.css\` (WITH the /public/ prefix)
1082
+
1083
+ ---
1084
+
1085
+ ## 3. WRONG Template Folder
1086
+
1087
+ CMS templates go in \`templates/\`, NOT \`collections/\`.
1088
+
1089
+ **WRONG:**
1090
+ \`\`\`
1091
+ collections/
1092
+ ├── posts/
1093
+ │ ├── index.html
1094
+ │ └── detail.html
1095
+ \`\`\`
1096
+
1097
+ **CORRECT:**
1098
+ \`\`\`
1099
+ templates/
1100
+ ├── posts_index.html
1101
+ └── posts_detail.html
1102
+ \`\`\`
1103
+
1104
+ Template naming: \`{collection}_index.html\` and \`{collection}_detail.html\`
1105
+
1106
+ ---
1107
+
1108
+ ## 4. MISSING Inline Editing
1109
+
1110
+ Without \`data-edit-key\`, users cannot edit text in the visual editor!
1111
+
1112
+ **WRONG (no inline editing):**
1113
+ \`\`\`html
1114
+ <h1>{{page_title}}</h1>
1115
+ <p>{{intro_text}}</p>
1116
+ \`\`\`
1117
+
1118
+ **CORRECT (enables inline editing):**
1119
+ \`\`\`html
1120
+ <h1 data-edit-key="page_title">{{page_title}}</h1>
1121
+ <p data-edit-key="intro_text">{{intro_text}}</p>
1122
+ \`\`\`
1123
+
1124
+ Every editable text element in static pages should have a unique \`data-edit-key\`.
1125
+
1126
+ ---
1127
+
1128
+ ## 5. Template URL Mismatches
1129
+
1130
+ Make sure links in templates match the manifest's path configuration.
1131
+
1132
+ **WRONG:**
1133
+ \`\`\`json
1134
+ // manifest.json
1135
+ "postsDetailPath": "/blog"
1136
+ \`\`\`
1137
+ \`\`\`html
1138
+ <!-- template -->
1139
+ <a href="/posts/{{slug}}">Read more</a> ← Wrong path!
1140
+ \`\`\`
1141
+
1142
+ **CORRECT:**
1143
+ \`\`\`json
1144
+ // manifest.json
1145
+ "postsDetailPath": "/blog"
1146
+ \`\`\`
1147
+ \`\`\`html
1148
+ <!-- template -->
1149
+ <a href="{{url}}">Read more</a> ← Uses built-in {{url}} token
1150
+ \`\`\`
1151
+
1152
+ **TIP:** Use \`{{url}}\` instead of hardcoding paths - it automatically uses the manifest config.
1153
+
1154
+ ---
1155
+
1156
+ ## 6. Field Naming Conventions
1157
+
1158
+ Use \`snake_case\` for field slugs, not camelCase.
1159
+
1160
+ **WRONG:**
1161
+ \`\`\`
1162
+ heroImage
1163
+ authorBio
1164
+ publishedDate
1165
+ \`\`\`
1166
+
1167
+ **CORRECT:**
1168
+ \`\`\`
1169
+ hero_image
1170
+ author_bio
1171
+ published_date
1172
+ \`\`\`
1173
+
1174
+ ---
1175
+
1176
+ ## 7. Inlining CSS/JS (Last Resort)
1177
+
1178
+ If external CSS/JS absolutely won't load, you can inline them in manifest.json:
1179
+
1180
+ \`\`\`json
1181
+ {
1182
+ "defaultHeadHtml": "<style>/* all CSS here */</style>",
1183
+ "defaultBodyEndHtml": "<script>/* all JS here */</script>"
1184
+ }
1185
+ \`\`\`
1186
+
1187
+ **But try fixing the /public/ folder first** - inlining makes updates harder.
1188
+
1189
+ ---
1190
+
1191
+ ## 8. Keeping Original Form Handlers
1192
+
1193
+ The source site often has JavaScript that "handles" form submissions with fake success messages.
1194
+
1195
+ **WRONG (original site's JS):**
1196
+ \`\`\`javascript
1197
+ form.addEventListener('submit', (e) => {
1198
+ e.preventDefault();
1199
+ showToast('Message sent!'); // FAKE! Data goes nowhere!
1200
+ });
1201
+ \`\`\`
1202
+
1203
+ **CORRECT (submit to Fast Mode):**
1204
+ \`\`\`html
1205
+ <form data-form-name="contact" action="/_forms/contact" method="POST">
1206
+ <!-- inputs -->
1207
+ </form>
1208
+ \`\`\`
1209
+ Then REMOVE the original JavaScript handler, or replace it with one that actually POSTs to \`/_forms/{formName}\`.
1210
+
1211
+ **See \`get_conversion_guide(section: "forms")\` for detailed form handling instructions.**
1212
+
1213
+ ---
1214
+
1215
+ ## Validation Workflow
1216
+
1217
+ Before deploying, ALWAYS run:
1218
+
1219
+ 1. \`validate_manifest\` - Catches format errors
1220
+ 2. \`validate_template\` - Catches token errors
1221
+ 3. \`validate_package\` - Full structure check
1222
+
1223
+ These tools will catch most of the above mistakes before deployment.`,
1224
+ full: '', // Will be assembled from all sections
1225
+ };
1226
+ /**
1227
+ * Returns the conversion guide, either full or a specific section
1228
+ */
1229
+ async function getConversionGuide(section) {
1230
+ if (section === 'full') {
1231
+ return `# Complete Website Conversion Guide
1232
+
1233
+ ---
1234
+
1235
+ ## WORKFLOW OVERVIEW (Follow This Order)
1236
+
1237
+ | Step | Action | Tools to Use |
1238
+ |------|--------|--------------|
1239
+ | 1. SETUP | Call list_projects → Ask user for project → Get projectId | \`list_projects\`, \`create_site\` |
1240
+ | 2. SCHEMA | Get existing schema OR plan new collections | \`get_tenant_schema\`, \`sync_schema\` |
1241
+ | 3. ANALYZE | Map URLs, identify collections, document assets | (manual) |
1242
+ | 4. BUILD | Create manifest.json, pages/, templates/, public/ | \`get_example\` |
1243
+ | 5. VALIDATE | Check all files before deploying | \`validate_manifest\`, \`validate_template\`, \`validate_package\` |
1244
+ | 6. DEPLOY | Upload package to project | \`deploy_package\` |
1245
+ | 7. CONTENT | Optionally manage CMS content | \`create_cms_item\`, \`list_cms_items\`, \`update_cms_item\` |
1246
+
1247
+ **DO NOT skip steps. You MUST have a projectId before you can deploy.**
1248
+
1249
+ ## CONTENT MANAGEMENT TOOLS
1250
+
1251
+ After deployment, you can manage CMS content directly:
1252
+
1253
+ | Tool | Description |
1254
+ |------|-------------|
1255
+ | \`create_cms_item\` | Create a new item (blog post, team member, etc.) |
1256
+ | \`list_cms_items\` | List items in a collection with optional filters |
1257
+ | \`get_cms_item\` | Get a single item by slug |
1258
+ | \`update_cms_item\` | Update an existing item |
1259
+ | \`delete_cms_item\` | Delete an item (**requires user confirmation**) |
1260
+
1261
+ **⚠️ DELETE SAFETY:** Never delete without asking the user first. The \`delete_cms_item\` tool requires \`confirmDelete: true\` which you should only set after explicit user permission.
1262
+
1263
+ ---
1264
+
1265
+ ${SECTIONS.first_steps}
1266
+ ---
1267
+
1268
+ ${SECTIONS.common_mistakes}
1269
+
1270
+ ---
1271
+
1272
+ ${SECTIONS.analysis}
1273
+
1274
+ ---
1275
+
1276
+ ${SECTIONS.structure}
1277
+
1278
+ ---
1279
+
1280
+ ${SECTIONS.seo}
1281
+
1282
+ ---
1283
+
1284
+ ${SECTIONS.manifest}
1285
+
1286
+ ---
1287
+
1288
+ ${SECTIONS.templates}
1289
+
1290
+ ---
1291
+
1292
+ ${SECTIONS.tokens}
1293
+
1294
+ ---
1295
+
1296
+ ${SECTIONS.forms}
1297
+
1298
+ ---
1299
+
1300
+ ${SECTIONS.assets}
1301
+
1302
+ ---
1303
+
1304
+ ${SECTIONS.checklist}
1305
+
1306
+ ---
1307
+
1308
+ ## Need More Help?
1309
+
1310
+ Use these MCP tools during conversion:
1311
+
1312
+ - \`get_field_types\` - Get available field types for sync_schema
1313
+ - \`get_tenant_schema\` - Get exact collections and fields for a specific project
1314
+ - \`get_example\` - Get code examples for specific patterns
1315
+ - \`validate_manifest\` - Check your manifest.json
1316
+ - \`validate_template\` - Check template token usage
1317
+ - \`validate_package\` - Full package validation
1318
+ - \`generate_sample_items\` - Create placeholder items after creating new collections (handles relation dependencies automatically)
1319
+
1320
+ **Validate as you work** - don't wait until the end!`;
1321
+ }
1322
+ return SECTIONS[section] || `Section not found: ${section}`;
1323
+ }