vibetachyon 1.5.8 → 1.6.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 (2) hide show
  1. package/dist/mcp-server.js +445 -106
  2. package/package.json +1 -1
@@ -173,37 +173,46 @@ async function validateTokenAtStartup() {
173
173
  async function startMcpServer() {
174
174
  await validateTokenAtStartup();
175
175
  const server = new mcp_js_1.McpServer({
176
- name: "VibeTachyon MCP",
177
- version: "1.5.0"
176
+ name: "VibeTachyon MCP — Animmaster Engine",
177
+ version: "1.6.0"
178
178
  });
179
179
  /**
180
- * VIBETACHYON FRONTEND PERSONA — Senior Frontend Designer
180
+ * VIBETACHYON FRONTEND PERSONA — Senior Frontend Designer (Animmaster Engine)
181
181
  *
182
- * You are a Senior Frontend Designer with 10 years of experience building SaaS products.
183
- * You have strong opinions and you enforce them. You do NOT generate generic code.
182
+ * You are a Senior Frontend Designer powered by the Animmaster component system.
183
+ * Animmaster is a curated set of 42 premium vanilla JS/SCSS/GSAP components that work
184
+ * as a unified system — not as isolated snippets. You do NOT generate generic code.
184
185
  *
185
186
  * YOUR MANDATORY WORKFLOW — follow this EXACT sequence for every page/section request:
186
- * 1. vibe_project_dna — read the project first, always
187
- * 2. vibe_design_brief — if no brief exists, ASK the user before touching any code
187
+ * 1. vibe_project_dna — read the project first, always
188
+ * 2. vibe_design_brief — if no brief exists, ASK the user before touching any code
188
189
  * 3. vibe_read_design_brief — read active brief before each new section
189
- * 4. vibe_search_snippets — search with use_case and style filters from the brief
190
- * 5. Generate code only after steps 1-4 are complete
190
+ * 4. vibe_search_snippets — search Animmaster components by use_case and visual_tone
191
+ * 5. vibe_get_component get the complete bundle (JS+HTML+SCSS) for chosen components
192
+ * 6. Generate code — only after steps 1-5 are complete
193
+ *
194
+ * ANIMMASTER SYSTEM RULES:
195
+ * - Components must be initialized in init_order sequence (0→6): preloader first, then cursor/header, then effects
196
+ * - Always respect compatible_with: do NOT combine components that aren't listed as compatible
197
+ * - animation_budget is 1-5: total page budget should not exceed 10. Hero can have budget 4-5, rest 1-2.
198
+ * - Each component comes with standalone JS (no build system needed), HTML template, and SCSS
199
+ * - CDN deps: GSAP (gsap.min.js), SplitType, Swiper, Chart.js — load only what's needed
200
+ * - data-attributes activate Animmaster components: always include them in the HTML
191
201
  *
192
202
  * YOUR NON-NEGOTIABLE RULES:
193
203
  * - NEVER mix more than 2 animation styles on a single page
194
204
  * - NEVER use more than 3 brand colors (primary, secondary, accent)
195
- * - NEVER hardcode hex/rgb — always use CSS variables or Tailwind semantic classes
205
+ * - NEVER hardcode hex/rgb — always use CSS variables
196
206
  * - NEVER generate a section without first checking the active design brief
197
207
  * - ALWAYS explain WHY you chose each component (1 sentence per component)
198
- * - ALWAYS build sections in the order defined in the brief
208
+ * - ALWAYS build sections in init_order (preloader cursor header → effects)
199
209
  * - ALWAYS reject components that violate the brief's avoid list
200
- * - If a component from vibe_search_snippets conflicts with the brief, search again with different terms
201
210
  *
202
211
  * VISUAL COHERENCE RULES:
203
212
  * - Dark brief → all sections dark. No random light cards in a dark page.
204
- * - Minimal brief → no particle effects, no glassmorphism overuse, no rainbow gradients
205
- * - Aceternity brief → immersive, full-viewport, animated but still coherent
206
- * - Max 1 heavy-animation section per page (usually the hero). Rest = subtle or static.
213
+ * - Minimal brief → animation_budget max 2 per section, no heavy GSAP timelines
214
+ * - Immersive brief → use parallax + mouse tracking + splittype for full effect
215
+ * - Max 1 heavy-animation section per page (hero). Rest = subtle or static.
207
216
  *
208
217
  * CONVERSION RULES (SaaS pages):
209
218
  * - Section order: hero → social-proof → features → how-it-works → pricing → faq → cta
@@ -211,17 +220,17 @@ async function startMcpServer() {
211
220
  * - Social proof (logos/testimonials) before pricing — builds trust first
212
221
  * - Every section must have ONE clear action or message. Not two.
213
222
  */
214
- // Tool: Searching Snippets
215
- server.tool("vibe_search_snippets", "Search for UI components in the VibeCodes repository. Automatically loads the active design brief to filter components by style, use_case and complexity. Always call vibe_project_dna and vibe_design_brief before this tool.", {
216
- query: zod_1.z.string().describe("Search query for the UI component (e.g. 'hero section dark', 'animated button')"),
223
+ // Tool: Searching Snippets (Animmaster Engine)
224
+ server.tool("vibe_search_snippets", "Search for Animmaster UI components. Returns standalone JS + HTML + SCSS bundles ready to use. Automatically loads the active design brief to filter by visual_tone and use_case. Always call vibe_project_dna and vibe_design_brief before this tool.", {
225
+ query: zod_1.z.string().describe("Search query for the UI component (e.g. 'hero section', 'text animation', 'parallax scroll')"),
217
226
  limit: zod_1.z.number().optional().describe("Max number of results to return (default: 5)"),
218
227
  projectDir: zod_1.z.string().optional().describe("Absolute path to the user's project root."),
219
- useCaseFilter: zod_1.z.string().optional().describe("Filter by use case: 'hero', 'cta', 'feature-section', 'navigation', 'footer', 'pricing', 'social-proof', 'background-effect', 'text-effect', 'modal', 'form'"),
220
- styleFilter: zod_1.z.array(zod_1.z.string()).optional().describe("Filter by style tags: 'dark', 'light', 'animated', 'glassmorphism', 'gradient', 'minimal', 'modern'"),
221
- complexityFilter: zod_1.z.enum(['simple', 'medium', 'complex']).optional().describe("Filter by code complexity")
228
+ useCaseFilter: zod_1.z.string().optional().describe("Filter by use case: 'hero', 'cta', 'feature-section', 'navigation', 'footer', 'pricing', 'social-proof', 'background-effect', 'text-effect', 'modal', 'form', 'preloader', 'cursor'"),
229
+ styleFilter: zod_1.z.array(zod_1.z.string()).optional().describe("Filter by visual tone: 'dark', 'light', 'animated', 'minimal', 'modern', 'immersive'"),
230
+ complexityFilter: zod_1.z.enum(['simple', 'medium', 'complex']).optional().describe("Filter by animation budget: simple=1-2, medium=3, complex=4-5")
222
231
  }, async ({ query, limit = 5, projectDir, useCaseFilter, styleFilter, complexityFilter }) => {
223
232
  await checkSanity();
224
- console.error(`[VibeTachyon] Searching DB for: ${query}`);
233
+ console.error(`[VibeTachyon] Searching Animmaster DB for: ${query}`);
225
234
  const rawRoot = projectDir || process.cwd();
226
235
  const root = await findProjectRoot(rawRoot);
227
236
  // Auto-load design brief to enrich filters
@@ -259,107 +268,191 @@ async function startMcpServer() {
259
268
  resolvedStyleFilter = derivedTags;
260
269
  }
261
270
  try {
262
- const rpcParams = {
263
- search_query: query,
264
- max_limit: limit
265
- };
266
- if (resolvedUseCaseFilter)
267
- rpcParams.filter_use_case = resolvedUseCaseFilter;
268
- if (resolvedStyleFilter && resolvedStyleFilter.length > 0)
269
- rpcParams.filter_style_tags = resolvedStyleFilter;
270
- if (resolvedComplexity)
271
- rpcParams.filter_complexity = resolvedComplexity;
272
- const { data: allResults, error } = await supabase
273
- .rpc('search_vibecodes_mcp', rpcParams);
271
+ // Build Animmaster query
272
+ let queryBuilder = supabase
273
+ .from('animmaster_components')
274
+ .select('name, category, description, html_template, js_standalone, scss_styles, npm_deps, core_deps, init_order, compatible_with, animation_budget, visual_tone, use_cases, data_attributes')
275
+ .eq('is_active', true);
276
+ // Text search across name and description
277
+ if (query) {
278
+ queryBuilder = queryBuilder.or(`name.ilike.%${query}%,description.ilike.%${query}%`);
279
+ }
280
+ // Use case filter
281
+ if (resolvedUseCaseFilter) {
282
+ queryBuilder = queryBuilder.contains('use_cases', [resolvedUseCaseFilter]);
283
+ }
284
+ // Visual tone / style filter
285
+ if (resolvedStyleFilter && resolvedStyleFilter.length > 0) {
286
+ queryBuilder = queryBuilder.overlaps('visual_tone', resolvedStyleFilter);
287
+ }
288
+ // Animation budget filter (complexity mapping)
289
+ if (resolvedComplexity) {
290
+ if (resolvedComplexity === 'simple')
291
+ queryBuilder = queryBuilder.lte('animation_budget', 2);
292
+ else if (resolvedComplexity === 'medium')
293
+ queryBuilder = queryBuilder.eq('animation_budget', 3);
294
+ else if (resolvedComplexity === 'complex')
295
+ queryBuilder = queryBuilder.gte('animation_budget', 4);
296
+ }
297
+ queryBuilder = queryBuilder.order('animation_budget').limit(limit);
298
+ const { data: allResults, error } = await queryBuilder;
274
299
  if (error) {
275
- console.error("[VibeTachyon] Supabase RPC Error:", error);
300
+ console.error("[VibeTachyon] Supabase Error:", error);
276
301
  throw error;
277
302
  }
278
- // If filtered search returns nothing, fallback to unfiltered
303
+ // If filtered search returns nothing, fallback to unfiltered text search
279
304
  let results = allResults;
280
305
  let fallbackNote = '';
281
306
  if ((!results || results.length === 0) && (resolvedStyleFilter || resolvedUseCaseFilter)) {
282
307
  console.error(`[VibeTachyon] No filtered results, falling back to unfiltered search`);
283
- const { data: fallback } = await supabase.rpc('search_vibecodes_mcp', {
284
- search_query: query,
285
- max_limit: limit
286
- });
287
- results = fallback;
288
- fallbackNote = `\n⚠️ No components matched the style filters. Showing best semantic matches instead — review fit with design brief.\n`;
308
+ const { data: fallback } = await supabase
309
+ .from('animmaster_components')
310
+ .select('name, category, description, html_template, js_standalone, scss_styles, npm_deps, core_deps, init_order, compatible_with, animation_budget, visual_tone, use_cases, data_attributes')
311
+ .eq('is_active', true)
312
+ .or(`name.ilike.%${query}%,description.ilike.%${query}%`)
313
+ .order('animation_budget')
314
+ .limit(limit);
315
+ results = fallback ?? [];
316
+ fallbackNote = `\n⚠️ No components matched the style/use-case filters. Showing best text matches instead — review fit with design brief.\n`;
289
317
  }
290
318
  if (!results || results.length === 0) {
291
319
  return {
292
- content: [{ type: "text", text: `No VibeCodes components found for "${query}". Try different search terms.` }]
320
+ content: [{ type: "text", text: `No Animmaster components found for "${query}". Try terms like: parallax, marquee, cursor, preloader, splittype, ripple, accordion, digcounter, slider, gallery.` }]
293
321
  };
294
322
  }
295
323
  // Coherence check — flag components that might break brief rules
296
324
  const briefAvoid = brief?.rules?.mustAvoid || [];
297
325
  const formattedResults = results.map((item) => {
298
- const srcInfo = item.source_table === 'Aceternity UI' ? '[⭐ ACETERNITY]'
299
- : item.source_table === 'Magic UI' ? '[✨ MAGIC UI]'
300
- : item.source_table === '21st.dev' ? '[🌐 21ST.DEV]'
301
- : `[📦 ${item.source_table?.toUpperCase() || 'VIBECODES'}]`;
302
- const styleLine = item.style_tags?.length
303
- ? `Style: ${item.style_tags.join(', ')}`
304
- : '';
305
- const useCaseLine = item.use_case?.length
306
- ? `Use case: ${item.use_case.join(', ')}`
307
- : '';
308
- const complexityLine = item.complexity ? `Complexity: ${item.complexity}` : '';
326
+ const budgetLabel = item.animation_budget <= 2 ? 'subtle' : item.animation_budget === 3 ? 'medium' : 'heavy';
327
+ const toneLine = item.visual_tone?.length ? `Tone: ${item.visual_tone.join(', ')}` : '';
328
+ const useCaseLine = item.use_cases?.length ? `Use cases: ${item.use_cases.join(', ')}` : '';
329
+ const budgetLine = `Animation budget: ${item.animation_budget}/5 (${budgetLabel})`;
330
+ const initLine = `Init order: ${item.init_order}`;
331
+ const compatLine = item.compatible_with?.length ? `Compatible with: ${item.compatible_with.join(', ')}` : '';
332
+ const depsLine = item.npm_deps?.length ? `CDN deps: ${item.npm_deps.join(', ')}` : '';
309
333
  // Coherence warning
310
- const codeStr = (item.code || '').toLowerCase();
311
- const violations = briefAvoid.filter((rule) => {
312
- const ruleKey = rule.toLowerCase();
313
- return codeStr.includes(ruleKey) || (item.title || '').toLowerCase().includes(ruleKey);
314
- });
334
+ const violations = briefAvoid.filter((rule) => (item.name || '').toLowerCase().includes(rule.toLowerCase()) ||
335
+ (item.description || '').toLowerCase().includes(rule.toLowerCase()));
315
336
  const coherenceNote = violations.length > 0
316
- ? `\n🚨 BRIEF CONFLICT: This component may contain "${violations.join(', ')}" which is in the avoid list. Review before using.\n`
337
+ ? `\n🚨 BRIEF CONFLICT: This component may conflict with "${violations.join(', ')}" in the avoid list. Review before using.\n`
317
338
  : '';
318
- const minifiedCode = minifySnippet(item.code);
319
- return `### ${item.title} ${srcInfo}\n${[styleLine, useCaseLine, complexityLine].filter(Boolean).join(' | ')}\n${item.description || ''}${coherenceNote}\n\`\`\`tsx\n${minifiedCode}\n\`\`\``;
339
+ const minifiedJs = minifySnippet(item.js_standalone || '');
340
+ const htmlSnippet = (item.html_template || '').split('\n').slice(0, 15).join('\n');
341
+ const scssSnippet = (item.scss_styles || '').split('\n').slice(0, 10).join('\n');
342
+ return [
343
+ `### ${item.name} [🎬 ANIMMASTER | ${item.category?.toUpperCase()}]`,
344
+ [toneLine, useCaseLine, budgetLine, initLine].filter(Boolean).join(' | '),
345
+ item.description || '',
346
+ compatLine,
347
+ depsLine,
348
+ coherenceNote,
349
+ `**HTML template:**\n\`\`\`html\n${htmlSnippet}\n\`\`\``,
350
+ `**JS standalone:**\n\`\`\`js\n${minifiedJs}\n\`\`\``,
351
+ scssSnippet ? `**SCSS (preview):**\n\`\`\`scss\n${scssSnippet}\n...\n\`\`\`` : '',
352
+ `→ Call vibe_get_component("${item.name}") for the complete SCSS and full metadata.`
353
+ ].filter(Boolean).join('\n');
320
354
  }).join('\n\n---\n\n');
321
- // Framework guardrails
322
- let frameworkContext = "";
323
- try {
324
- const pkgPath = path_1.default.join(root, 'package.json');
325
- if (await fs_extra_1.default.pathExists(pkgPath)) {
326
- const pkg = await fs_extra_1.default.readJson(pkgPath);
327
- const deps = { ...pkg.dependencies, ...pkg.devDependencies };
328
- const isVite = !!deps['vite'];
329
- const isNext = !!deps['next'];
330
- frameworkContext += `\n[FRAMEWORK: ${isNext ? 'Next.js' : isVite ? 'Vite/React' : 'React'}]\n`;
331
- if (isVite)
332
- frameworkContext += `- Remove "use client", replace next/image with <img>, replace next/link with <a>\n`;
333
- if (isNext)
334
- frameworkContext += `- Add "use client" if component uses hooks or event handlers\n`;
335
- }
336
- }
337
- catch { /* silent */ }
338
- // Peer dep audit
339
- const standardDeps = { "framer-motion": "11.0.0", "lucide-react": "0.400.0" };
340
- const audit = await auditPeerDeps(root, standardDeps);
341
- const auditPrefix = audit.warnings.length > 0
342
- ? `⚠️ [VIBESHIELD]\n${audit.warnings.join('\n')}\n\n`
343
- : '';
344
355
  const briefSummary = brief
345
356
  ? `[Active Brief: ${brief.referenceStyle} | ${brief.rules?.visualTone} | max ${brief.rules?.maxAnimationTypes} animations]\n`
346
357
  : '';
347
358
  return {
348
359
  content: [{
349
360
  type: "text",
350
- text: `${auditPrefix}${briefWarning}${fallbackNote}${briefSummary}` +
351
- `🧠 VibeTachyon found ${results.length} components for "${query}"` +
352
- (resolvedStyleFilter ? ` [style: ${resolvedStyleFilter.join(', ')}]` : '') +
361
+ text: `${briefWarning}${fallbackNote}${briefSummary}` +
362
+ `🎬 VibeTachyon Animmaster found ${results.length} components for "${query}"` +
363
+ (resolvedStyleFilter ? ` [tone: ${resolvedStyleFilter.join(', ')}]` : '') +
353
364
  (resolvedUseCaseFilter ? ` [use: ${resolvedUseCaseFilter}]` : '') +
354
- `:\n\n${formattedResults}\n\n${frameworkContext}` +
355
- `\nINSTRUCTION FOR AI: Cross-check each component against the active design brief before using. Reject any that violate the brief rules.`
365
+ `:\n\n${formattedResults}\n\n` +
366
+ `INSTRUCTION FOR AI:\n` +
367
+ `- Initialize components in init_order sequence (lower = first)\n` +
368
+ `- Check compatible_with before combining components on one page\n` +
369
+ `- Use vibe_get_component(name) to get the complete SCSS bundle\n` +
370
+ `- Respect animation_budget: page total should not exceed 10`
356
371
  }]
357
372
  };
358
373
  }
359
374
  catch (error) {
360
375
  console.error("[VibeTachyon] Search Error:", error);
361
376
  return {
362
- content: [{ type: "text", text: `Error searching VibeCodes repository: ${error instanceof Error ? error.message : String(error)}` }]
377
+ content: [{ type: "text", text: `Error searching Animmaster repository: ${error instanceof Error ? error.message : String(error)}` }]
378
+ };
379
+ }
380
+ });
381
+ // Tool: Get Complete Animmaster Component Bundle
382
+ server.tool("vibe_get_component", "Fetch the complete Animmaster component bundle by name: full JS (standalone), complete SCSS, HTML template, CDN deps list, init order, and compatible components. Call this after vibe_search_snippets to get the full implementation code.", {
383
+ name: zod_1.z.string().describe("Animmaster component name (e.g. 'parallax', 'cursor', 'marquee', 'splittype', 'preloader', 'ripple')")
384
+ }, async ({ name }) => {
385
+ await checkSanity();
386
+ try {
387
+ const { data, error } = await supabase
388
+ .from('animmaster_components')
389
+ .select('name, category, description, html_template, js_standalone, scss_styles, npm_deps, core_deps, init_order, init_trigger, compatible_with, incompatible_with, animation_budget, visual_tone, use_cases, data_attributes')
390
+ .eq('name', name)
391
+ .single();
392
+ if (error || !data) {
393
+ // Try partial match
394
+ const { data: partial } = await supabase
395
+ .from('animmaster_components')
396
+ .select('name, category, description, html_template, js_standalone, scss_styles, npm_deps, core_deps, init_order, compatible_with, animation_budget, visual_tone, use_cases')
397
+ .ilike('name', `%${name}%`)
398
+ .eq('is_active', true)
399
+ .limit(1)
400
+ .single();
401
+ if (!partial) {
402
+ return { content: [{ type: "text", text: `Component "${name}" not found in Animmaster. Use vibe_search_snippets to find available components.` }] };
403
+ }
404
+ return buildComponentResponse(partial);
405
+ }
406
+ return buildComponentResponse(data);
407
+ }
408
+ catch (err) {
409
+ return { content: [{ type: "text", text: `Error fetching component: ${err}` }] };
410
+ }
411
+ function buildComponentResponse(item) {
412
+ const budgetLabel = item.animation_budget <= 2 ? 'subtle' : item.animation_budget === 3 ? 'medium' : 'heavy';
413
+ const dataAttrsStr = item.data_attributes
414
+ ? Object.entries(item.data_attributes).map(([k, v]) => ` ${k}: ${JSON.stringify(v)}`).join('\n')
415
+ : 'none';
416
+ return {
417
+ content: [{
418
+ type: "text",
419
+ text: [
420
+ `# ${item.name} [🎬 ANIMMASTER | ${item.category?.toUpperCase()}]`,
421
+ `**Description:** ${item.description || 'No description'}`,
422
+ `**Animation budget:** ${item.animation_budget}/5 (${budgetLabel})`,
423
+ `**Init order:** ${item.init_order} (initialize in this sequence on page)`,
424
+ `**Init trigger:** ${item.init_trigger || 'load'}`,
425
+ `**Visual tone:** ${item.visual_tone?.join(', ') || 'universal'}`,
426
+ `**Use cases:** ${item.use_cases?.join(', ') || 'general'}`,
427
+ `**CDN dependencies:** ${item.npm_deps?.length ? item.npm_deps.join(', ') : 'none'}`,
428
+ `**Core deps (Animmaster):** ${item.core_deps?.length ? item.core_deps.join(', ') : 'none'}`,
429
+ `**Compatible with:** ${item.compatible_with?.length ? item.compatible_with.join(', ') : 'all'}`,
430
+ `**Incompatible with:** ${item.incompatible_with?.length ? item.incompatible_with.join(', ') : 'none'}`,
431
+ `**Data attributes:**\n${dataAttrsStr}`,
432
+ '',
433
+ `## HTML Template`,
434
+ `\`\`\`html`,
435
+ item.html_template || '<!-- No HTML template -->',
436
+ `\`\`\``,
437
+ '',
438
+ `## JS Standalone (no imports needed)`,
439
+ `\`\`\`js`,
440
+ item.js_standalone || '// No standalone JS',
441
+ `\`\`\``,
442
+ '',
443
+ `## SCSS Styles`,
444
+ `\`\`\`scss`,
445
+ item.scss_styles || '/* No SCSS */',
446
+ `\`\`\``,
447
+ '',
448
+ `INSTRUCTION FOR AI:`,
449
+ `- Include the HTML template in the page structure`,
450
+ `- Add the JS standalone code in a <script> tag (after DOM ready)`,
451
+ `- Include the SCSS/CSS in your stylesheet`,
452
+ `- Install CDN deps: ${item.npm_deps?.join(', ') || 'none required'}`,
453
+ `- This component initializes at order ${item.init_order} — load after lower-order components`
454
+ ].join('\n')
455
+ }]
363
456
  };
364
457
  }
365
458
  });
@@ -981,21 +1074,28 @@ COMPONENT SELECTION FILTER (apply mentally before using any component):
981
1074
  await checkSanity();
982
1075
  try {
983
1076
  let allResults = [];
984
- for (const query of componentNames) {
985
- const { data, error } = await supabase
986
- .rpc('search_vibecodes_mcp', { search_query: query, max_limit: 1 });
987
- if (!error && data && data.length > 0) {
1077
+ for (const compName of componentNames) {
1078
+ const { data } = await supabase
1079
+ .from('animmaster_components')
1080
+ .select('name, category, description, html_template, js_standalone, scss_styles, npm_deps, init_order, compatible_with, animation_budget')
1081
+ .or(`name.eq.${compName},name.ilike.%${compName}%`)
1082
+ .eq('is_active', true)
1083
+ .limit(1);
1084
+ if (data && data.length > 0) {
988
1085
  allResults.push(data[0]);
989
1086
  }
990
1087
  }
991
1088
  if (allResults.length === 0) {
992
- return { content: [{ type: "text", text: `Could not find any of the requested components in the VibeCodes registry.` }] };
1089
+ return { content: [{ type: "text", text: `Could not find any of the requested components in the Animmaster registry.` }] };
993
1090
  }
1091
+ // Sort by init_order so they're listed in initialization sequence
1092
+ allResults.sort((a, b) => (a.init_order || 5) - (b.init_order || 5));
994
1093
  const formattedResults = allResults.map((item) => {
995
- return `### Sub-Component: ${item.title}\n\`\`\`tsx\n${item.code}\n\`\`\``;
1094
+ const minifiedJs = minifySnippet(item.js_standalone || '');
1095
+ return `### ${item.name} [🎬 ANIMMASTER | init_order: ${item.init_order}]\nCategory: ${item.category} | Budget: ${item.animation_budget}/5\nDeps: ${item.npm_deps?.join(', ') || 'none'}\n\n**HTML:**\n\`\`\`html\n${item.html_template || ''}\n\`\`\`\n\n**JS:**\n\`\`\`js\n${minifiedJs}\n\`\`\``;
996
1096
  }).join('\n\n---\n\n');
997
1097
  return {
998
- content: [{ type: "text", text: `[DEPENDENCY MATRIX RESOLVED]\n\nINSTRUCTION FOR AI: The following sub-components are required. Create their files in the appropriate UI folder before integrating the main component.\n\n${formattedResults}` }]
1098
+ content: [{ type: "text", text: `[ANIMMASTER BUNDLE RESOLVED — ${allResults.length} components, sorted by init_order]\n\nINSTRUCTION FOR AI: Initialize these components in the order listed (lowest init_order first).\n\n${formattedResults}` }]
999
1099
  };
1000
1100
  }
1001
1101
  catch (err) {
@@ -1032,16 +1132,51 @@ COMPONENT SELECTION FILTER (apply mentally before using any component):
1032
1132
  }, async ({ themeOrIndustry }) => {
1033
1133
  await checkSanity();
1034
1134
  try {
1035
- const sections = ['navbar', 'hero section', 'features grid', 'pricing table', 'footer'];
1036
- let combinedPage = `[VIBEPAGE COMPOSER: ${themeOrIndustry}]\n\nINSTRUCTION FOR AI: Assemble a complete page using the following 5 elite components.\n\n`;
1037
- for (const section of sections) {
1038
- const query = `${themeOrIndustry} ${section}`;
1039
- const { data, error } = await supabase
1040
- .rpc('search_vibecodes_mcp', { search_query: query, max_limit: 1 });
1041
- if (!error && data && data.length > 0) {
1042
- combinedPage += `### === ${section.toUpperCase()} ===\n**Title:** ${data[0].title}\n\`\`\`tsx\n${data[0].code}\n\`\`\`\n\n`;
1135
+ // Map sections to Animmaster use cases and search terms
1136
+ const sectionMap = [
1137
+ { section: 'preloader', query: 'preloader', useCase: 'preloader' },
1138
+ { section: 'cursor', query: 'cursor', useCase: 'cursor' },
1139
+ { section: 'navbar', query: 'header navigation', useCase: 'navigation' },
1140
+ { section: 'hero', query: `hero ${themeOrIndustry}`, useCase: 'hero' },
1141
+ { section: 'features', query: 'features section', useCase: 'feature-section' },
1142
+ { section: 'footer', query: 'footer', useCase: 'footer' }
1143
+ ];
1144
+ let combinedPage = `[🎬 ANIMMASTER PAGE COMPOSER: ${themeOrIndustry}]\n\nINSTRUCTION FOR AI: Assemble a complete page using Animmaster components in init_order sequence.\n\n`;
1145
+ const allComponents = [];
1146
+ for (const { section, query, useCase } of sectionMap) {
1147
+ const { data } = await supabase
1148
+ .from('animmaster_components')
1149
+ .select('name, category, description, html_template, js_standalone, npm_deps, init_order, animation_budget, compatible_with')
1150
+ .eq('is_active', true)
1151
+ .contains('use_cases', [useCase])
1152
+ .limit(1);
1153
+ if (data && data.length > 0) {
1154
+ allComponents.push({ section, ...data[0] });
1155
+ }
1156
+ else {
1157
+ // Fallback: text search
1158
+ const { data: fallback } = await supabase
1159
+ .from('animmaster_components')
1160
+ .select('name, category, description, html_template, js_standalone, npm_deps, init_order, animation_budget, compatible_with')
1161
+ .eq('is_active', true)
1162
+ .or(`name.ilike.%${query}%,description.ilike.%${query}%`)
1163
+ .limit(1);
1164
+ if (fallback && fallback.length > 0)
1165
+ allComponents.push({ section, ...fallback[0] });
1043
1166
  }
1044
1167
  }
1168
+ // Sort by init_order
1169
+ allComponents.sort((a, b) => (a.init_order || 5) - (b.init_order || 5));
1170
+ for (const item of allComponents) {
1171
+ const minifiedJs = minifySnippet(item.js_standalone || '');
1172
+ combinedPage += `### === ${item.section.toUpperCase()} → ${item.name} [init_order: ${item.init_order}] ===\n`;
1173
+ combinedPage += `Budget: ${item.animation_budget}/5 | Deps: ${item.npm_deps?.join(', ') || 'none'}\n\n`;
1174
+ combinedPage += `**HTML:**\n\`\`\`html\n${item.html_template || ''}\n\`\`\`\n\n`;
1175
+ combinedPage += `**JS:**\n\`\`\`js\n${minifiedJs}\n\`\`\`\n\n`;
1176
+ }
1177
+ const totalBudget = allComponents.reduce((sum, c) => sum + (c.animation_budget || 0), 0);
1178
+ combinedPage += `\n---\nTotal animation budget: ${totalBudget}/30 | Components: ${allComponents.length}\n`;
1179
+ combinedPage += `\nINSTRUCTION FOR AI: Initialize scripts in the order listed above (preloader → cursor → header → effects).`;
1045
1180
  return { content: [{ type: "text", text: combinedPage }] };
1046
1181
  }
1047
1182
  catch (err) {
@@ -3898,6 +4033,210 @@ ${consequences.length > 0 ? consequences.map(c => `- ${c}`).join('\n') : '- (non
3898
4033
  return { content: [{ type: "text", text: `[VIBE REFACTOR ERROR] ${err.message || err}` }] };
3899
4034
  }
3900
4035
  });
4036
+ // Tool: Page Composer — Full Page Orchestration
4037
+ server.tool("vibe_compose_page", "Orchestrates a complete page build: reads the design brief, plans section rhythm, searches components per section with coherence rules, and returns a full page assembly plan with component assignments and visual rhythm. Call this instead of manually building section by section.", {
4038
+ projectDir: zod_1.z.string().describe("Absolute path to the project root."),
4039
+ overrideSections: zod_1.z.array(zod_1.z.string()).optional().describe("Override section list. If omitted, uses sections from the active design brief.")
4040
+ }, async ({ projectDir, overrideSections }) => {
4041
+ await checkSanity();
4042
+ const root = await findProjectRoot(projectDir || process.cwd());
4043
+ // 1. Load design brief
4044
+ const sessionPath = path_1.default.join(root, '.vibetachyon', 'design-session.json');
4045
+ if (!(await fs_extra_1.default.pathExists(sessionPath))) {
4046
+ return {
4047
+ content: [{
4048
+ type: "text",
4049
+ text: `[VibeTachyon Page Composer] No design brief found.\n\n` +
4050
+ `INSTRUCTION FOR AI: Stop and call vibe_design_brief first. Ask the user:\n` +
4051
+ `1. What is the goal of this page?\n` +
4052
+ `2. What is the niche/product?\n` +
4053
+ `3. Who is the target audience?\n` +
4054
+ `4. Which visual style? (linear / vercel / stripe / aceternity / apple / notion)\n` +
4055
+ `5. Which sections are needed?`
4056
+ }]
4057
+ };
4058
+ }
4059
+ const brief = await fs_extra_1.default.readJson(sessionPath);
4060
+ const sections = overrideSections || brief.sectionsNeeded || ['hero', 'features', 'cta'];
4061
+ const tone = brief.rules?.visualTone || 'dark';
4062
+ const refStyle = brief.referenceStyle || 'custom';
4063
+ const maxAnimations = brief.rules?.maxAnimationTypes ?? 2;
4064
+ const avoid = brief.rules?.mustAvoid || [];
4065
+ // 2. Visual rhythm — alternate animated/static to avoid visual fatigue
4066
+ const rhythmMap = {
4067
+ hero: 'animated',
4068
+ 'social-proof': 'static',
4069
+ features: 'subtle',
4070
+ 'how-it-works': 'subtle',
4071
+ pricing: 'static',
4072
+ testimonials: 'static',
4073
+ faq: 'static',
4074
+ cta: 'animated',
4075
+ footer: 'static',
4076
+ 'background-effect': 'animated',
4077
+ navigation: 'subtle',
4078
+ modal: 'subtle',
4079
+ form: 'static'
4080
+ };
4081
+ // Enforce max animation budget
4082
+ let animationBudget = maxAnimations;
4083
+ const sectionRhythm = {};
4084
+ for (const section of sections) {
4085
+ const natural = rhythmMap[section] || 'subtle';
4086
+ if (natural === 'animated' && animationBudget > 0) {
4087
+ sectionRhythm[section] = 'animated';
4088
+ animationBudget--;
4089
+ }
4090
+ else if (natural === 'animated' && animationBudget === 0) {
4091
+ sectionRhythm[section] = 'subtle'; // downgrade — budget exhausted
4092
+ }
4093
+ else {
4094
+ sectionRhythm[section] = natural;
4095
+ }
4096
+ }
4097
+ // 3. Style filters per section
4098
+ const styleForSection = (section) => {
4099
+ const base = tone === 'dark' ? ['dark'] : tone === 'light' ? ['light'] : [];
4100
+ const rhythm = sectionRhythm[section];
4101
+ if (rhythm === 'animated')
4102
+ return [...base, 'animated'];
4103
+ if (rhythm === 'subtle')
4104
+ return [...base, 'modern'];
4105
+ return base;
4106
+ };
4107
+ // 4. Search queries per section
4108
+ const sectionQueries = {
4109
+ hero: `hero section ${refStyle} ${tone}`,
4110
+ 'social-proof': `testimonials logos social proof ${tone}`,
4111
+ features: `features grid cards ${tone}`,
4112
+ 'how-it-works': `steps process how it works ${tone}`,
4113
+ pricing: `pricing table plans ${tone}`,
4114
+ testimonials: `testimonials review cards ${tone}`,
4115
+ faq: `faq accordion ${tone}`,
4116
+ cta: `call to action banner ${tone}`,
4117
+ footer: `footer links ${tone}`,
4118
+ navigation: `navbar navigation ${tone}`,
4119
+ 'background-effect': `background effect ${refStyle}`,
4120
+ modal: `modal dialog ${tone}`,
4121
+ form: `form input ${tone}`
4122
+ };
4123
+ // 5. Build section plan (search each section)
4124
+ const sectionResults = [];
4125
+ let totalAnimationsUsed = 0;
4126
+ for (const section of sections) {
4127
+ const query = sectionQueries[section] || `${section} ${tone}`;
4128
+ const styleFilter = styleForSection(section);
4129
+ const rhythm = sectionRhythm[section];
4130
+ if (rhythm === 'animated')
4131
+ totalAnimationsUsed++;
4132
+ let amQueryBuilder = supabase
4133
+ .from('animmaster_components')
4134
+ .select('name, category, description, use_cases, visual_tone, animation_budget, init_order, compatible_with, npm_deps')
4135
+ .eq('is_active', true)
4136
+ .contains('use_cases', [section])
4137
+ .limit(2);
4138
+ if (styleFilter.length > 0) {
4139
+ amQueryBuilder = amQueryBuilder.overlaps('visual_tone', styleFilter);
4140
+ }
4141
+ const { data: results } = await amQueryBuilder;
4142
+ const found = results && results.length > 0;
4143
+ const topMatch = found ? results[0] : null;
4144
+ // Coherence check
4145
+ let coherenceFlag = '';
4146
+ if (topMatch && avoid.length > 0) {
4147
+ const violations = avoid.filter((rule) => (topMatch.name || '').toLowerCase().includes(rule.toLowerCase()) ||
4148
+ (topMatch.description || '').toLowerCase().includes(rule.toLowerCase()));
4149
+ if (violations.length > 0) {
4150
+ coherenceFlag = ` ⚠️ BRIEF CONFLICT (${violations.join(', ')})`;
4151
+ }
4152
+ }
4153
+ sectionResults.push(`## Section ${sections.indexOf(section) + 1}: ${section.toUpperCase()}\n` +
4154
+ `Rhythm: ${rhythm} | Tone filters: ${styleFilter.join(', ') || 'none'}\n` +
4155
+ (found && topMatch
4156
+ ? `✅ Best match: "${topMatch.name}" [🎬 ANIMMASTER | init_order: ${topMatch.init_order}]${coherenceFlag}\n` +
4157
+ ` Tone: ${topMatch.visual_tone?.join(', ') || 'universal'} | Budget: ${topMatch.animation_budget}/5\n` +
4158
+ ` Compatible with: ${topMatch.compatible_with?.join(', ') || 'all'}\n` +
4159
+ ` Search query used: "${query}"`
4160
+ : `⚠️ No Animmaster component found for "${query}" with filters [${styleFilter.join(', ')}]\n` +
4161
+ ` → Run: vibe_search_snippets(query="${section}", useCaseFilter="${section}") with broader terms`));
4162
+ }
4163
+ // 6. Save page state to .vibetachyon/page-state.json
4164
+ const pageState = {
4165
+ createdAt: new Date().toISOString(),
4166
+ brief: { goal: brief.pageGoal, style: refStyle, tone },
4167
+ sections: sections.map((s, i) => ({
4168
+ name: s,
4169
+ order: i + 1,
4170
+ rhythm: sectionRhythm[s],
4171
+ status: 'planned'
4172
+ })),
4173
+ animationsUsed: totalAnimationsUsed,
4174
+ animationBudget: maxAnimations,
4175
+ coherenceScore: Math.round((1 - avoid.length / 10) * 100)
4176
+ };
4177
+ await fs_extra_1.default.writeJson(path_1.default.join(root, '.vibetachyon', 'page-state.json'), pageState, { spaces: 2 });
4178
+ // 7. Final coherence report
4179
+ const budgetWarning = totalAnimationsUsed > maxAnimations
4180
+ ? `\n🚨 ANIMATION BUDGET EXCEEDED: ${totalAnimationsUsed} animated sections (max: ${maxAnimations}). Downgraded automatically.\n`
4181
+ : `\n✅ Animation budget respected: ${totalAnimationsUsed}/${maxAnimations} animated sections.\n`;
4182
+ return {
4183
+ content: [{
4184
+ type: "text",
4185
+ text: `[VibeTachyon Page Composer]\n\n` +
4186
+ `PAGE: ${brief.pageGoal || 'Landing page'}\n` +
4187
+ `STYLE: ${refStyle} | TONE: ${tone} | SECTIONS: ${sections.length}\n` +
4188
+ `${budgetWarning}\n` +
4189
+ `SECTION PLAN:\n${sectionResults.join('\n\n')}\n\n` +
4190
+ `STATE SAVED: .vibetachyon/page-state.json\n\n` +
4191
+ `INSTRUCTION FOR AI — EXECUTION ORDER:\n` +
4192
+ sections.map((s, i) => ` ${i + 1}. Build "${s}" section (${sectionRhythm[s]}) — call vibe_search_snippets(query="${sectionQueries[s] || s}", useCaseFilter="${s}", styleFilter=${JSON.stringify(styleForSection(s))})`).join('\n') +
4193
+ `\n\nRULES:\n` +
4194
+ `- Build sections in THIS exact order\n` +
4195
+ `- For each section: search → review coherence → generate → move to next\n` +
4196
+ `- Do NOT skip sections or reorder them\n` +
4197
+ `- If a component violates the brief, search again before using it`
4198
+ }]
4199
+ };
4200
+ });
4201
+ // Tool: Mark Section Complete
4202
+ server.tool("vibe_section_done", "Marks a page section as complete in the page state. Call this after finishing each section to track progress and maintain build momentum.", {
4203
+ projectDir: zod_1.z.string().describe("Absolute path to the project root."),
4204
+ sectionName: zod_1.z.string().describe("The section name that was just completed. e.g. 'hero', 'features', 'cta'"),
4205
+ componentUsed: zod_1.z.string().optional().describe("Name of the component that was used for this section.")
4206
+ }, async ({ projectDir, sectionName, componentUsed }) => {
4207
+ await checkSanity();
4208
+ const root = await findProjectRoot(projectDir || process.cwd());
4209
+ const statePath = path_1.default.join(root, '.vibetachyon', 'page-state.json');
4210
+ if (!(await fs_extra_1.default.pathExists(statePath))) {
4211
+ return {
4212
+ content: [{ type: "text", text: `No page state found. Run vibe_compose_page first.` }]
4213
+ };
4214
+ }
4215
+ const state = await fs_extra_1.default.readJson(statePath);
4216
+ const section = state.sections?.find((s) => s.name === sectionName);
4217
+ if (section) {
4218
+ section.status = 'done';
4219
+ if (componentUsed)
4220
+ section.componentUsed = componentUsed;
4221
+ section.completedAt = new Date().toISOString();
4222
+ await fs_extra_1.default.writeJson(statePath, state, { spaces: 2 });
4223
+ }
4224
+ const done = state.sections?.filter((s) => s.status === 'done') || [];
4225
+ const planned = state.sections?.filter((s) => s.status === 'planned') || [];
4226
+ const next = planned[0];
4227
+ return {
4228
+ content: [{
4229
+ type: "text",
4230
+ text: `✅ Section "${sectionName}" marked as done.\n` +
4231
+ `Progress: ${done.length}/${state.sections?.length || 0} sections complete.\n` +
4232
+ (next
4233
+ ? `\nNext section: "${next.name}" (${next.rhythm})\n` +
4234
+ `INSTRUCTION FOR AI: Call vibe_read_design_brief then build the "${next.name}" section.`
4235
+ : `\n🎉 ALL SECTIONS COMPLETE! Page is ready.\n` +
4236
+ `INSTRUCTION FOR AI: Run a final coherence review — check that all sections share the same visual tone and no animation budget was exceeded.`)
4237
+ }]
4238
+ };
4239
+ });
3901
4240
  // Connect via stdio
3902
4241
  const transport = new stdio_js_1.StdioServerTransport();
3903
4242
  await server.connect(transport);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibetachyon",
3
- "version": "1.5.8",
3
+ "version": "1.6.0",
4
4
  "description": "VibeCodes MCP CLI Installer and Server",
5
5
  "main": "dist/index.js",
6
6
  "bin": {